/******************************************************************************* * Copyright (c) 2010 Eric Bodden. * 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: * Eric Bodden - initial API and implementation ******************************************************************************/ package de.bodden.tamiflex.resolution; import java.util.Collections; import java.util.HashSet; import java.util.Set; import org.eclipse.core.resources.IProject; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.jdt.core.IClassFile; import org.eclipse.jdt.core.ICompilationUnit; import org.eclipse.jdt.core.IJavaProject; import org.eclipse.jdt.core.IMethod; import org.eclipse.jdt.core.IType; import org.eclipse.jdt.core.JavaCore; import org.eclipse.jdt.core.JavaModelException; import org.eclipse.jdt.core.Signature; import org.eclipse.jdt.core.ToolFactory; import org.eclipse.jdt.core.dom.AST; import org.eclipse.jdt.core.dom.ASTParser; import org.eclipse.jdt.core.dom.ASTVisitor; import org.eclipse.jdt.core.dom.CompilationUnit; import org.eclipse.jdt.core.dom.MethodDeclaration; import org.eclipse.jdt.core.util.IClassFileReader; import org.eclipse.jdt.core.util.ICodeAttribute; import org.eclipse.jdt.core.util.ILineNumberAttribute; import org.eclipse.jdt.core.util.IMethodInfo; /** * Provides functionality to revolve methods by name and line number. */ public class MethodResolver { protected static Set<IMethod> resolvedMethods; /** * Tries to resolve an {@link IMethod} by name and line number. * If the the information is insufficient then a set of all possibly matching methods is returned. * @param containerClassName the name of the class declaring the method * @param methodName the method's name * @param containerProject the project containing the class declaring the method * @param lineNumber potentially a line number contained within the method, may otherwise be -1 * @return */ public static Set<IMethod> findMatchingMethods(String containerClassName, String methodName, IProject containerProject, int lineNumber) { resolvedMethods = new HashSet<IMethod>(); IJavaProject javaProject = JavaCore.create(containerProject); try { IType type = javaProject.findType(containerClassName, (IProgressMonitor) null); if(type!=null) { String methodOrConstructorName = methodName.equals("<init>") ? type.getElementName() : methodName; for(IMethod method: type.getMethods()) { if(method.getElementName().equals(methodOrConstructorName)) { resolvedMethods.add(method); } } if(resolvedMethods.size()>1) { //found multiple methods with this name; use line number info to find the right one disambiguateMethodByLineNumber(containerClassName, methodName, containerProject, lineNumber); } } return resolvedMethods; } catch (JavaModelException e) { e.printStackTrace(); return Collections.emptySet(); } } private static void disambiguateMethodByLineNumber(String containerClassName, String methodName, IProject containerProject, int lineNumber) { IJavaProject javaProject = JavaCore.create(containerProject); try { IType type = javaProject.findType(containerClassName,(IProgressMonitor)null); ICompilationUnit compilationUnit = type.getCompilationUnit(); if(compilationUnit!=null) { ASTParser parser = ASTParser.newParser(AST.JLS3); parser.setSource(compilationUnit); parser.setResolveBindings(true); CompilationUnit cu = (CompilationUnit) parser.createAST(null); final int linePosition = cu.getPosition(lineNumber, 0); cu.accept(new ASTVisitor() { public boolean visit(MethodDeclaration method) { if(method.getStartPosition()<=linePosition && method.getStartPosition()+method.getLength()>=linePosition) { //method is resolved resolvedMethods.clear(); resolvedMethods.add((IMethod) method.resolveBinding().getJavaElement()); } return false; } }); } else { IClassFile classFile = type.getClassFile(); if(classFile!=null) { IClassFileReader reader = ToolFactory.createDefaultClassFileReader( classFile, IClassFileReader.METHOD_INFOS | IClassFileReader.METHOD_BODIES ); for(IMethodInfo method : reader.getMethodInfos()) { String currMethodName = new String(method.getName()); if(!currMethodName.equals(methodName)) continue; ICodeAttribute codeAttribute = method.getCodeAttribute(); ILineNumberAttribute lineNumberAttribute = codeAttribute.getLineNumberAttribute(); if(lineNumberAttribute!=null) { int[][] lineNumberTable = lineNumberAttribute.getLineNumberTable(); if(lineNumberTable!=null && lineNumberTable.length>0) { int startLine = Integer.MAX_VALUE; int endLine = 0; for (int[] entry : lineNumberTable) { int line = entry[1]; startLine = Math.min(startLine, line); endLine = Math.max(endLine, line); } if(startLine >= lineNumber && endLine <= lineNumber) { char[][] parameterTypes = Signature.getParameterTypes(method.getDescriptor()); String[] parameterTypeNames = new String[parameterTypes.length]; int i=0; for (char[] cs : parameterTypes) { parameterTypeNames[i] = new String(cs); i++; } IMethod iMethod = type.getMethod(currMethodName, parameterTypeNames); //method resolved resolvedMethods.clear(); resolvedMethods.add(iMethod); } } } } } } } catch (JavaModelException e) { e.printStackTrace(); } } }