/*******************************************************************************
* Copyright (c) 2009 the CHISEL group and contributors.
* 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:
* Del Myers - initial API and implementation
*******************************************************************************/
package ca.uvic.chisel.javasketch.internal;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.TreeMap;
import java.util.TreeSet;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.core.runtime.SubProgressMonitor;
import org.eclipse.jdt.core.ICompilationUnit;
import org.eclipse.jdt.core.IJavaElement;
import org.eclipse.jdt.core.IMember;
import org.eclipse.jdt.core.IMethod;
import org.eclipse.jdt.core.IType;
import org.eclipse.jdt.core.ITypeHierarchy;
import org.eclipse.jdt.core.JavaModelException;
import org.eclipse.jdt.core.Signature;
import org.eclipse.jdt.core.search.IJavaSearchConstants;
import org.eclipse.jdt.core.search.IJavaSearchScope;
import org.eclipse.jdt.core.search.SearchEngine;
import org.eclipse.jdt.core.search.SearchMatch;
import org.eclipse.jdt.core.search.SearchParticipant;
import org.eclipse.jdt.core.search.SearchPattern;
import org.eclipse.jdt.core.search.SearchRequestor;
import ca.uvic.chisel.hsqldb.server.IDataPortal;
import ca.uvic.chisel.javasketch.IProgramSketch;
import ca.uvic.chisel.javasketch.SketchPlugin;
import ca.uvic.chisel.javasketch.data.model.IActivation;
import ca.uvic.chisel.javasketch.data.model.IMessage;
import ca.uvic.chisel.javasketch.data.model.IThread;
import ca.uvic.chisel.javasketch.data.model.ITrace;
import ca.uvic.chisel.javasketch.data.model.ITraceClass;
import ca.uvic.chisel.javasketch.data.model.ITraceClassMethod;
import ca.uvic.chisel.javasketch.data.model.ITraceModel;
import ca.uvic.chisel.javasketch.data.model.imple.internal.ThreadImpl;
import ca.uvic.chisel.javasketch.utils.JavaSketchUtilities;
/**
* Utility class to aid in relating trace model elements to java elements.
* @author Del Myers
*
*/
public class JavaSearchUtils {
private static TreeMap<String, IType> cachedTypes = new TreeMap<String, IType>();
private static final String METHOD_QUERY = "SELECT method_signature, arrival_id, model_id FROM Activation WHERE type_name=? AND method_name=? AND thread_id=?";
private static final String CLASS_QUERY = "SELECT arrival_id, model_id FROM Activation WHERE type_name=? and thread_id=?";
// private static final String ARRIVAL_TIME_QUERY = "SELECT time, order_num FROM MESSAGE where model_id=?";
/**
* @author Del Myers
*
*/
private static final class LocalSearchRequestor extends SearchRequestor {
List<SearchMatch> matches = new ArrayList<SearchMatch>();
@Override
public void acceptSearchMatch(SearchMatch match) throws CoreException {
if (match.isExact()) {
matches.add(match);
}
}
}
public static SearchPattern createMethodDeclarationPattern(ITraceClassMethod method) {
if (method == null) {
return null;
}
ITraceClass callingClass = method.getTraceClass();
if (callingClass == null) {
return null;
}
String className = callingClass.getName();
className = Signature.toString(className);
boolean isConstructor = false;
String methodName = method.getName();
isConstructor = methodName.startsWith("<init");
if (isConstructor) {
int lastDotIndex = className.lastIndexOf('.');
if (lastDotIndex >=0) {
methodName = className.substring(lastDotIndex+1);
}
}
String methodString = Signature.toString(method.getSignature(), methodName, null, false, true);
String returnType = "";
if (!isConstructor) {
int returnIndex = methodString.indexOf(' ');
if (returnIndex > 0) {
returnType = " " + methodString.substring(0,returnIndex);
methodString = methodString.substring(returnIndex+1, methodString.length());
}
}
return SearchPattern.createPattern(
className + '.' + methodString + returnType,
IJavaSearchConstants.METHOD,
IJavaSearchConstants.DECLARATIONS,
SearchPattern.R_EXACT_MATCH);
}
/**
* Tries to find a java element corresponding to the given model element.
* @param traceElement
* @param monitor
* @return
* @throws InterruptedException
* @throws CoreException
*/
public static IJavaElement findElement(ITraceModel traceElement, IProgressMonitor monitor) throws InterruptedException, CoreException {
if (traceElement == null) {
return null;
}
IProgramSketch sketch = SketchPlugin.getDefault().getSketch(traceElement);
if (sketch == null) {
return null;
}
IJavaSearchScope scope = JavaSketchUtilities.createJavaSearchScope(sketch);
if (scope == null) {
return null;
}
if (traceElement instanceof ITraceClass) {
try {
String qualifiedName = ((ITraceClass)traceElement).getName();
return searchForType(qualifiedName, scope, monitor);
} catch (IllegalArgumentException e) {
e.printStackTrace();
return null;
}
} else {
ITraceClassMethod method = null;
if (traceElement instanceof ITraceClassMethod) {
method = (ITraceClassMethod) traceElement;
} else if (traceElement instanceof IMessage) {
IMessage call = (IMessage) traceElement;
IActivation activation = call.getActivation();
if (activation == null) {
return null;
}
method = activation.getMethod();
} else if (traceElement instanceof IActivation) {
IActivation activation = (IActivation) traceElement;
method = activation.getMethod();
}
if (method != null) {
return searchForMethod(method, scope, monitor);
}
}
return null;
}
public static final boolean areMethodsSimilar(ITraceClassMethod method1, ITraceClassMethod method2) {
if ((method1 == method2) && method1 == null) {
return true;
}
if (method1 == null || method2 == null) {
return false;
}
if (!method1.getName().equals(method2.getName())) {
return false;
}
if (!method1.getSignature().equals(method2.getSignature())) {
return false;
}
try {
IJavaElement element1 = findElement(method1.getTraceClass(), new NullProgressMonitor());
IJavaElement element2 = findElement(method2.getTraceClass(), new NullProgressMonitor());
if (element1 instanceof IType && element2 instanceof IType) {
IType type1 = (IType) element1;
IType type2 = (IType) element2;
if (type1.getFullyQualifiedName().equals(type2.getFullyQualifiedName())) {
return true;
}
ITypeHierarchy hierarchy1 = type1.newTypeHierarchy(new NullProgressMonitor());
ITypeHierarchy hierarchy2 = type2.newTypeHierarchy(new NullProgressMonitor());
for (IType h1 : hierarchy1.getAllClasses()) {
for (IType h2: hierarchy2.getAllClasses()) {
if (h1.getFullyQualifiedName().equals(h2.getFullyQualifiedName())) {
return true;
}
}
}
}
} catch (InterruptedException e) {
} catch (CoreException e) {
}
return false;
}
private static IJavaElement searchForMethod(ITraceClassMethod method,
IJavaSearchScope scope, IProgressMonitor monitor) throws CoreException, InterruptedException {
monitor.beginTask("Searching for method", 200);
String classSignature = method.getTraceClass().getName();
try {
classSignature = Signature.toString(classSignature);
} catch (IllegalArgumentException e) {}
String methodName = method.getName();
String signature = method.getSignature();
return searchForMethod(scope, monitor, classSignature, methodName,
signature);
}
/**
* @param scope
* @param monitor
* @param classSignature
* @param methodName
* @param signature
* @return
* @throws CoreException
* @throws InterruptedException
*/
public static IJavaElement searchForMethod(IJavaSearchScope scope,
IProgressMonitor monitor, String classSignature, String methodName,
String signature) throws CoreException, InterruptedException {
SubProgressMonitor typeMonitor = new SubProgressMonitor(monitor, 100);
SubProgressMonitor hierarchyMonitor = new SubProgressMonitor(monitor, 100);
IType foundType = (IType) searchForType(classSignature, scope, typeMonitor);
if (foundType == null) return null;
boolean isConstructor = false;
if (methodName.startsWith("<init")) {
isConstructor = true;
boolean i = isConstructor;
boolean isAnonymous = false;
if (!foundType.exists()) {
//check anonymity using the dollar sign
String elementName = foundType.getElementName();
if (elementName.length() > 0 && Character.isDigit(elementName.charAt(0))) {
isAnonymous = true;
}
} else {
isAnonymous = foundType.isAnonymous();
}
if (isAnonymous) {
methodName = "";
} else {
methodName = foundType.getElementName();
}
}
String[] methodParamTypes = Signature.getParameterTypes(signature);
IMethod searchMethod = foundType.getMethod(methodName, methodParamTypes);
IMethod[] methods = foundType.getMethods();
for (IMethod checkMethod : methods) {
if (isSimilar(searchMethod, checkMethod)) {
return checkMethod;
}
}
return searchMethod;
}
public static boolean isSimilar(IMethod method1, IMethod method2) {
String[] myParams = method1.getParameterTypes();
String[] theirParams = method2.getParameterTypes();
int myParamsLength = myParams.length;
String[] simpleNames = new String[myParamsLength];
for (int i = 0; i < myParamsLength; i++) {
String erasure = Signature.getTypeErasure(myParams[i]);
simpleNames[i] = Signature.getSimpleName(Signature.toString(erasure));
}
String name = method1.getElementName();
if (name.equals(method2.getElementName())) {
if (myParamsLength == theirParams.length) {
for (int i = 0; i < myParamsLength; i++) {
String mySimpleName = simpleNames[i];
String simpleName2 = Signature.getSimpleName(Signature.toString(Signature.getTypeErasure(theirParams[i])));
//first, check for generics. If my param is not generic,
//but theirs is, it is a match.
int myTypeKind = Signature.getTypeSignatureKind(myParams[i]);
int theirTypeKind = Signature.getTypeSignatureKind(theirParams[i]);
switch (theirTypeKind) {
case Signature.TYPE_VARIABLE_SIGNATURE:
case Signature.WILDCARD_TYPE_SIGNATURE:
case Signature.CAPTURE_TYPE_SIGNATURE:
switch (myTypeKind) {
case Signature.CLASS_TYPE_SIGNATURE:
case Signature.ARRAY_TYPE_SIGNATURE:
continue;
}
}
//otherwise, try and match exactly
if (!mySimpleName.equals(simpleName2)) {
return false;
}
}
return true;
}
}
return false;
}
private static IJavaElement searchForType(String classSignature, IJavaSearchScope scope, IProgressMonitor monitor) throws CoreException, InterruptedException {
try {
synchronized (cachedTypes) {
IType found = cachedTypes.get(classSignature);
if (found != null) {
return found;
}
SearchEngine engine = new SearchEngine();
SearchPattern pattern = null;
SearchParticipant participant = SearchEngine.getDefaultSearchParticipant();
LocalSearchRequestor requestor = new LocalSearchRequestor();
pattern = SearchPattern.createPattern(
classSignature.replace('$', '.'),
IJavaSearchConstants.CLASS,
IJavaSearchConstants.DECLARATIONS,
SearchPattern.R_EXACT_MATCH);
engine.search(pattern, new SearchParticipant[] {participant}, scope, requestor, monitor);
if (requestor.matches.size() > 0) {
found = (IType) requestor.matches.get(0).getElement();
if (found.getFullyQualifiedName().equals(classSignature)) {
cachedTypes.put(classSignature, found);
return found;
}
}
String[] className = classSignature.split("\\$");
found = cachedTypes.get(className[0]);
if (found == null) {
//find the class
pattern = SearchPattern.createPattern(
className[0],
IJavaSearchConstants.CLASS,
IJavaSearchConstants.DECLARATIONS,
SearchPattern.R_EXACT_MATCH);
engine.search(pattern, new SearchParticipant[] {participant}, scope, requestor, monitor);
if (monitor.isCanceled()) {
throw new InterruptedException();
}
if (requestor.matches.size() > 0) {
found = (IType) requestor.matches.get(0).getElement();
if (found.getFullyQualifiedName().equals(classSignature)) {
for (SearchMatch match : requestor.matches) {
IType temp = (IType) match.getElement();
if (temp.getTypeRoot() instanceof ICompilationUnit) {
//prefer source types.
found = temp;
break;
}
}
if (cachedTypes.size() > 100) {
cachedTypes.clear();
}
cachedTypes.put(className[0], found);
} else {
found = null;
}
}
}
if (found == null)
return null;
StringBuilder childTypeName = new StringBuilder();
childTypeName.append(className[0]);
//check each of the indexes for the sub-types
for (int i = 1; i < className.length; i++) {
childTypeName.append('$');
childTypeName.append(className[i]);
IType parent = found;
found = cachedTypes.get(childTypeName.toString());
if (found == null) {
boolean isAnonymous = false;
Integer occurrenceCount = -1;
try {
occurrenceCount = Integer.parseInt(className[i]);
isAnonymous = true;
} catch (NumberFormatException e) {
isAnonymous = false;
}
if (isAnonymous) {
if (!parent.isBinary()) {
found = parent.getType("", occurrenceCount);
if (found != null) {
if (found.exists()) {
cachedTypes.put(childTypeName.toString(), found);
continue;
} else {
found = null;
}
}
} else {
//if it is a binary type, there is no hope for
//finding an anonymous inner class. Use the number
//as the type name, and cache the handle.
found = parent.getType(className[i]);
cachedTypes.put(childTypeName.toString(), found);
continue;
}
}
ArrayList<IType> childTypes = new ArrayList<IType>();
LinkedList<IJavaElement> children = new LinkedList<IJavaElement>();
children.addAll(Arrays.asList(parent.getChildren()));
while (children.size() > 0) {
IJavaElement child = children.removeFirst();
if (child instanceof IType) {
childTypes.add((IType) child);
} else if (child instanceof IMember) {
children.addAll(Arrays.asList(((IMember)child).getChildren()));
}
}
int numIndex = 0;
while (numIndex < className[i].length() && Character.isDigit(className[i].charAt(numIndex))) {
numIndex++;
}
String name = className[i];
try {
//get a number at the beginning to find out if
//there is an occurrence count
if (numIndex <= name.length()) {
occurrenceCount = parseInt(name.substring(0, numIndex));
if (occurrenceCount == null) {
occurrenceCount = 1;
}
if (numIndex < name.length()-1) {
name = name.substring(numIndex);
} else {
name = "";
}
}
for (IType childType : childTypes) {
if ("".equals(name)) {
if (childType.getOccurrenceCount() == occurrenceCount) {
found = childType;
break;
}
} else {
if (name.equals(childType.getElementName()) && childType.getOccurrenceCount() == occurrenceCount) {
found = childType;
break;
}
}
}
if (found == null) {
if ("".equals(name)) {
found = parent.getTypeRoot().getJavaProject().findType(classSignature);
//found = parent.getType("" + occurrenceCount);
} else {
found = parent.getType(name, occurrenceCount);
}
}
cachedTypes.put(childTypeName.toString(), found);
} catch (Exception e) {
e.printStackTrace();
}
}
}
return found;
}
} catch (Exception e) {
SketchPlugin.getDefault().log(e);
return null;
}
}
/**
* @param string
* @return the integer value of the string, or null if it cannot be parsed as an integer
*/
private static Integer parseInt(String string) {
try {
return Integer.valueOf(string);
} catch (NumberFormatException e) {}
return null;
}
/**
* Searches the given thread for activations that match the given java method.
* @param thread the thread to search.
* @param method the method to check.
* @param monitor this is a long-running process, and should be done inside a monitor.
* @return a list of found activations
* @throws CoreException if an error occurred during retrieval of the activations
*/
public static List<IActivation> findActivationsForMethod(IThread thread, IMethod element, IProgressMonitor monitor) throws CoreException {
DBProgramSketch sketch = (DBProgramSketch) SketchPlugin.getDefault().getSketch(thread);
ArrayList<IActivation> activations = new ArrayList<IActivation>();
if (sketch == null) {
return activations;
}
Exception ex = null;
monitor.beginTask("Searching for matching activations", IProgressMonitor.UNKNOWN);
try {
PreparedStatement methodStatement = sketch.getPortal().prepareStatement(METHOD_QUERY);
IType methodType = element.getDeclaringType();
String qualifiedName = methodType.getFullyQualifiedName();
String methodName = element.getElementName();
methodStatement.setString(1, qualifiedName);
methodStatement.setString(2, methodName);
methodStatement.setLong(3, ((ThreadImpl)thread).getModelID());
ResultSet results = methodStatement.executeQuery();
while (results.next()) {
if (monitor.isCanceled()) break;
String signature = results.getString(1);
//try to find a method that matches in the declaring type
IMethod matchMethod = methodType.getMethod(methodName, Signature.getParameterTypes(signature));
if (element.isSimilar(matchMethod)) {
long model_id = results.getLong("model_id");
IActivation a = ((ThreadImpl)thread).getByModelID(model_id);
if (a != null) {
activations.add(a);
}
}
}
} catch (IllegalArgumentException e) {
ex=e;
} catch (SQLException e) {
ex=e;
}
monitor.done();
if (ex != null) {
throw new CoreException(SketchPlugin.getDefault().createStatus(ex));
}
return activations;
}
public static List<IActivation> findActivationsForClass(IThread thread, IType element, IProgressMonitor monitor) throws CoreException {
ArrayList<IActivation> activations = new ArrayList<IActivation>();
DBProgramSketch sketch = (DBProgramSketch) SketchPlugin.getDefault().getSketch(thread);
if (sketch == null) {
return activations;
}
try {
IDataPortal portal = sketch.getPortal();
String qualifiedName = element.getFullyQualifiedName();
PreparedStatement classStatement = portal.prepareStatement(CLASS_QUERY);
classStatement.setString(1, qualifiedName);
classStatement.setLong(2, ((ThreadImpl)thread).getModelID());
ResultSet results = classStatement.executeQuery();
while (results.next()) {
if (monitor.isCanceled()) break;
long model_id = results.getLong("model_id");
IActivation activation = ((ThreadImpl)thread).getByModelID(model_id);
if (activation != null) {
activations.add(activation);
}
}
} catch (SQLException e) {
throw new CoreException(SketchPlugin.getDefault().createStatus(e));
} finally {
monitor.done();
}
return activations;
}
public static String getFullyQualifiedName(IType type, boolean includeOccurrenceCount) {
IType declaring = type;
String qualifiedName = "";
try {
while (declaring != null) {
if (declaring.isAnonymous()) {
qualifiedName = declaring.getOccurrenceCount() + "" + qualifiedName;
} else {
qualifiedName = declaring.getElementName() + qualifiedName;
if (includeOccurrenceCount) {
IJavaElement parent = declaring.getParent();
if (parent instanceof IMember && !(parent instanceof IType)) {
qualifiedName = type.getOccurrenceCount() + qualifiedName;
}
}
}
IJavaElement parent = declaring.getParent();
while (parent != null) {
if (parent instanceof IType) {
declaring = (IType) parent;
break;
}
parent = parent.getParent();
}
if (parent != null) {
qualifiedName = "$" + qualifiedName;
} else {
declaring = null;
}
}
} catch (JavaModelException e) {
return type.getFullyQualifiedName();
}
String pack = type.getPackageFragment().getElementName();
if (!"".equals(pack)) pack = pack + ".";
qualifiedName = pack + qualifiedName;
return qualifiedName;
}
/**
* @param traceData
* @param selection
* @return
*/
public static IThread[] findThreadsForElement(ITrace trace,
IJavaElement element) {
HashSet<IThread> threads = new HashSet<IThread>();
IProgramSketch sketch = SketchPlugin.getDefault().getSketch(trace);
if (sketch != null) {
try {
if (sketch.getPortal() != null) {
PreparedStatement statement = null;
if (element instanceof IType) {
String typeName = getFullyQualifiedName((IType) element, true);
statement = sketch.getPortal().prepareStatement("SELECT DISTINCT thread_id from Activation WHERE type_name=?");
statement.setString(1, typeName);
} else if (element instanceof IMethod) {
IMethod method = (IMethod) element;
ITraceClassMethod tcm = findSimilarMethod(trace, method);
if (tcm != null) {
statement = sketch.getPortal().prepareStatement("SELECT DISTINCT thread_id from Activation WHERE type_name=? AND method_name=? AND method_signature=?");
statement.setString(1, tcm.getTraceClass().getName());
statement.setString(2, tcm.getName());
statement.setString(3, tcm.getSignature());
}
}
if (statement != null) {
ResultSet results = statement.executeQuery();
TreeSet<Long> modelIDs = new TreeSet<Long>();
while (results.next()) {
modelIDs.add(results.getLong(1));
}
if (modelIDs.size() > 0) {
for (IThread t : trace.getThreads()) {
if (t instanceof ThreadImpl) {
ThreadImpl thread = (ThreadImpl) t;
if (modelIDs.contains(thread.getModelID())) {
threads.add(thread);
}
}
}
}
}
}
} catch (CoreException e) {
} catch (SQLException e) {
}
}
return threads.toArray(new IThread[threads.size()]);
}
public static ITraceClassMethod findSimilarMethod(ITrace trace, IMethod method) {
IType declaringType = method.getDeclaringType();
IProgramSketch sketch = SketchPlugin.getDefault().getSketch(trace);
if (sketch != null) {
ITraceClass tc = findSimilarType(trace, declaringType);
if (tc != null) {
try {
PreparedStatement statement = sketch.getPortal().prepareStatement("SELECT method_signature FROM Method WHERE type_name=? AND method_name=?");
statement.setString(1, tc.getName());
statement.setString(2, method.getElementName());
ResultSet results = statement.executeQuery();
while (results.next()) {
String signature = results.getString(1);
IMethod compareMethod = declaringType.getMethod(method.getElementName(), Signature.getParameterTypes(signature));
if (method.isSimilar(compareMethod)) {
return tc.findMethod(method.getElementName(), signature);
}
}
} catch (IllegalArgumentException e) {
} catch (SQLException e) {
} catch (CoreException e) {
}
}
}
return null;
}
public static ITraceClass findSimilarType(ITrace trace, IType type) {
return trace.forName(getFullyQualifiedName(type, true));
}
}