package org.eclipse.jdt.internal.corext.callhierarchy;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.OperationCanceledException;
import org.eclipse.jdt.core.IJavaElement;
import org.eclipse.jdt.core.IMember;
import org.eclipse.jdt.core.IMethod;
import org.eclipse.jdt.core.ISourceRange;
import org.eclipse.jdt.core.IType;
import org.eclipse.jdt.core.JavaModelException;
import org.eclipse.jdt.core.dom.ASTNode;
import org.eclipse.jdt.core.dom.AbstractTypeDeclaration;
import org.eclipse.jdt.core.dom.AnonymousClassDeclaration;
import org.eclipse.jdt.core.dom.BodyDeclaration;
import org.eclipse.jdt.core.dom.ClassInstanceCreation;
import org.eclipse.jdt.core.dom.CompilationUnit;
import org.eclipse.jdt.core.dom.ConstructorInvocation;
import org.eclipse.jdt.core.dom.IMethodBinding;
import org.eclipse.jdt.core.dom.ITypeBinding;
import org.eclipse.jdt.core.dom.MethodDeclaration;
import org.eclipse.jdt.core.dom.MethodInvocation;
import org.eclipse.jdt.core.dom.SuperConstructorInvocation;
import org.eclipse.jdt.core.dom.SuperMethodInvocation;
import org.eclipse.jdt.core.search.IJavaSearchScope;
import org.eclipse.jdt.internal.core.manipulation.JavaManipulationPlugin;
import org.eclipse.jdt.internal.corext.dom.Bindings;
import org.eclipse.jdt.internal.corext.dom.HierarchicalASTVisitor;
import org.eclipse.jdt.internal.corext.util.JavaModelUtil;
class CalleeAnalyzerVisitor extends HierarchicalASTVisitor {
private final CallSearchResultCollector fSearchResults;
private final IMember fMember;
private final CompilationUnit fCompilationUnit;
private final IProgressMonitor fProgressMonitor;
private int fMethodEndPosition;
private int fMethodStartPosition;
CalleeAnalyzerVisitor(IMember member, CompilationUnit compilationUnit, IProgressMonitor progressMonitor) {
fSearchResults = new CallSearchResultCollector();
this.fMember = member;
this.fCompilationUnit= compilationUnit;
this.fProgressMonitor = progressMonitor;
try {
ISourceRange sourceRange = member.getSourceRange();
this.fMethodStartPosition = sourceRange.getOffset();
this.fMethodEndPosition = fMethodStartPosition + sourceRange.getLength();
} catch (JavaModelException jme) {
JavaManipulationPlugin.log(jme);
}
}
public Map<String, MethodCall> getCallees() {
return fSearchResults.getCallers();
}
@Override
public boolean visit(ClassInstanceCreation node) {
progressMonitorWorked(1);
if (!isFurtherTraversalNecessary(node)) {
return false;
}
if (isNodeWithinMethod(node)) {
addMethodCall(node.resolveConstructorBinding(), node);
}
return true;
}
@Override
public boolean visit(ConstructorInvocation node) {
progressMonitorWorked(1);
if (!isFurtherTraversalNecessary(node)) {
return false;
}
if (isNodeWithinMethod(node)) {
addMethodCall(node.resolveConstructorBinding(), node);
}
return true;
}
@Override
public boolean visit(AbstractTypeDeclaration node) {
progressMonitorWorked(1);
if (!isFurtherTraversalNecessary(node)) {
return false;
}
if (isNodeWithinMethod(node)) {
List<BodyDeclaration> bodyDeclarations= node.bodyDeclarations();
for (Iterator<BodyDeclaration> iter= bodyDeclarations.iterator(); iter.hasNext(); ) {
BodyDeclaration bodyDeclaration= iter.next();
if (bodyDeclaration instanceof MethodDeclaration) {
MethodDeclaration child= (MethodDeclaration) bodyDeclaration;
if (child.isConstructor()) {
addMethodCall(child.resolveBinding(), child.getName());
}
}
}
return false;
}
return true;
}
@Override
public boolean visit(MethodDeclaration node) {
progressMonitorWorked(1);
return isFurtherTraversalNecessary(node);
}
@Override
public boolean visit(MethodInvocation node) {
progressMonitorWorked(1);
if (!isFurtherTraversalNecessary(node)) {
return false;
}
if (isNodeWithinMethod(node)) {
addMethodCall(node.resolveMethodBinding(), node);
}
return true;
}
@Override
public boolean visit(SuperConstructorInvocation node) {
progressMonitorWorked(1);
if (!isFurtherTraversalNecessary(node)) {
return false;
}
if (isNodeWithinMethod(node)) {
addMethodCall(node.resolveConstructorBinding(), node);
}
return true;
}
@Override
public boolean visit(SuperMethodInvocation node) {
progressMonitorWorked(1);
if (!isFurtherTraversalNecessary(node)) {
return false;
}
if (isNodeWithinMethod(node)) {
addMethodCall(node.resolveMethodBinding(), node);
}
return true;
}
@Override
public boolean visit(AnonymousClassDeclaration node) {
return isNodeEnclosingMethod(node);
}
protected void addMethodCall(IMethodBinding calledMethodBinding, ASTNode node) {
try {
if (calledMethodBinding != null) {
fProgressMonitor.worked(1);
ITypeBinding calledTypeBinding = calledMethodBinding.getDeclaringClass();
IType calledType = null;
if (!calledTypeBinding.isAnonymous()) {
calledType = (IType) calledTypeBinding.getJavaElement();
} else {
if (!"java.lang.Object".equals(calledTypeBinding.getSuperclass().getQualifiedName())) {
calledType= (IType) calledTypeBinding.getSuperclass().getJavaElement();
} else {
calledType = (IType) calledTypeBinding.getInterfaces()[0].getJavaElement();
}
}
IMethod calledMethod = findIncludingSupertypes(calledMethodBinding,
calledType, fProgressMonitor);
IMember referencedMember= null;
if (calledMethod == null) {
if (calledMethodBinding.isConstructor() && calledMethodBinding.getParameterTypes().length == 0) {
referencedMember= calledType;
}
} else {
if (calledType.isInterface()) {
calledMethod = findImplementingMethods(calledMethod);
}
if (!isIgnoredBySearchScope(calledMethod)) {
referencedMember= calledMethod;
}
}
final int position= node.getStartPosition();
final int number= fCompilationUnit.getLineNumber(position);
fSearchResults.addMember(fMember, referencedMember, position, position + node.getLength(), number < 1 ? 1 : number);
}
} catch (JavaModelException jme) {
JavaManipulationPlugin.log(jme);
}
}
private static IMethod findIncludingSupertypes(IMethodBinding method, IType type, IProgressMonitor pm) throws JavaModelException {
IMethod inThisType= Bindings.findMethod(method, type);
if (inThisType != null)
return inThisType;
IType[] superTypes= JavaModelUtil.getAllSuperTypes(type, pm);
for (int i= 0; i < superTypes.length; i++) {
IMethod m= Bindings.findMethod(method, superTypes[i]);
if (m != null)
return m;
}
return null;
}
private boolean isIgnoredBySearchScope(IMethod enclosingElement) {
if (enclosingElement != null) {
return !getSearchScope().encloses(enclosingElement);
} else {
return false;
}
}
private IJavaSearchScope getSearchScope() {
return CallHierarchyCore.getDefault().getSearchScope();
}
private boolean isNodeWithinMethod(ASTNode node) {
int nodeStartPosition = node.getStartPosition();
int nodeEndPosition = nodeStartPosition + node.getLength();
if (nodeStartPosition < fMethodStartPosition) {
return false;
}
if (nodeEndPosition > fMethodEndPosition) {
return false;
}
return true;
}
private boolean isNodeEnclosingMethod(ASTNode node) {
int nodeStartPosition = node.getStartPosition();
int nodeEndPosition = nodeStartPosition + node.getLength();
if (nodeStartPosition < fMethodStartPosition && nodeEndPosition > fMethodEndPosition) {
return true;
}
return false;
}
private boolean isFurtherTraversalNecessary(ASTNode node) {
return isNodeWithinMethod(node) || isNodeEnclosingMethod(node);
}
private IMethod findImplementingMethods(IMethod calledMethod) {
Collection<IJavaElement> implementingMethods = CallHierarchyCore.getDefault()
.getImplementingMethods(calledMethod);
if ((implementingMethods.size() == 0) || (implementingMethods.size() > 1)) {
return calledMethod;
} else {
return (IMethod) implementingMethods.iterator().next();
}
}
private void progressMonitorWorked(int work) {
if (fProgressMonitor != null) {
fProgressMonitor.worked(work);
if (fProgressMonitor.isCanceled()) {
throw new OperationCanceledException();
}
}
}
}