JDTを使ってメソッドの呼び出し元を検索する方法

備忘録として。 eclipseとjdtを使ったメソッド呼び出し元検索のサンプルコード。

import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;

import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.equinox.app.IApplication;
import org.eclipse.equinox.app.IApplicationContext;
import org.eclipse.jdt.core.IJavaElement;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.IMember;
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.search.IJavaSearchScope;
import org.eclipse.jdt.core.search.SearchEngine;
import org.eclipse.jdt.internal.corext.callhierarchy.CallHierarchy;
import org.eclipse.jdt.internal.corext.callhierarchy.MethodWrapper;

public class Application implements IApplication {

    public Object start(IApplicationContext context) throws Exception {
        String projectName = "sample";
        String fqcn = "pkg.ClassA";
        String methodName = "methodA";

        IType type = findType(projectName, fqcn);
        IMember[] members = findMethod(type, methodName);

        for (IMember m : members) {
            CallTracer tracer = new CallTracer(m);
            traceCaller(tracer);

            tracer.trace();
            // tracer.traceSimple();
        }

        return IApplication.EXIT_OK;
    }

    private void traceCaller(CallTracer tracer) {
        IMember member = tracer.getTarget();

        CallerUtil util = new CallerUtil();
        Set<IMethod> methods = util.getCallers(member);

        for (Iterator<IMethod> ite = methods.iterator(); ite.hasNext();) {
            IMethod next = ite.next();

            CallTracer c = new CallTracer(next);
            tracer.addCaller(c);

            traceCaller(c);
        }
    }

    private IMethod[] findMethod(IType type, String methodName) throws JavaModelException {
        IMethod[] methods = type.getMethods();
        List<IMethod> result = new ArrayList<IMethod>();

        for (IMethod method : methods) {
            if (method.getElementName().equals(methodName)) {
                result.add(method);
            }
        }

        return result.toArray(new IMethod[result.size()]);
    }

    private IType findType(String projectName, String fullName) throws JavaModelException {
        IJavaProject jp = JavaCore.create(ResourcesPlugin.getWorkspace().getRoot().getProject(projectName));

        return jp.findType(fullName);
    }

    @Override
    public void stop() {
        // TODO Auto-generated method stub

    }

    class CallerUtil {

        public HashSet<IMethod> getCallers(IMember member) {
            CallHierarchy callHierarchy = CallHierarchy.getDefault();

            IJavaSearchScope scope = SearchEngine.createWorkspaceScope();
            callHierarchy.setSearchScope(scope);

            IMember[] members = { member };
            MethodWrapper[] methodWrappers = callHierarchy.getCallerRoots(members);
            HashSet<IMethod> result = new HashSet<IMethod>();

            for (MethodWrapper mw : methodWrappers) {
                MethodWrapper[] calls = mw.getCalls(new NullProgressMonitor());
                result.addAll(getMethods(calls));
            }

            return result;
        }

        private HashSet<IMethod> getMethods(MethodWrapper[] methodWrappers) {
            HashSet<IMethod> result = new HashSet<IMethod>();

            for (MethodWrapper mw : methodWrappers) {
                IMethod method = toMethod(mw);

                if (method != null) {
                    result.add(method);
                }
            }

            return result;
        }

        private IMethod toMethod(MethodWrapper methodWrappers) {
            IMember member = methodWrappers.getMember();

            if (member.getElementType() == IJavaElement.METHOD) {
                return (IMethod) methodWrappers.getMember();
            } else {
                System.out.println("IGNORE:" + member.toString());
            }

            return null;
        }
    }

    class CallTracer {
        
        private IMember target;
        private List<CallTracer> callers;

        public CallTracer(IMember member) {
            target = member;
            callers = new ArrayList<CallTracer>();
        }

        public void addCaller(CallTracer c) {
            callers.add(c);
        }

        public IMember getTarget() {
            return target;
        }

        
        public void trace() {
            trace(0);
        }

        private void trace(int idt) {
            printTrace(idt);

            for (CallTracer tracer : callers) {
                idt++;
                tracer.trace(idt);
            }
        }

        public void traceSimple() {
            traceSimple(true, 0);
        }

        public void traceSimple(boolean pltFlg, int idt) {
            if (pltFlg) {
                printTrace(idt);
                idt++;
            }

            if (callers.isEmpty()) {
                printTrace(idt);
            } else {
                for (CallTracer tracer : callers) {
                    tracer.traceSimple(false, idt);
                }
            }
        }

        private void printTrace(int idt) {
            System.out.println(getIdt(idt) + " class:" + target.getParent().getElementName());
            System.out.println(getIdt(idt) + "method:" + target.getElementName());
        }

        private String getIdt(int idt) {
            StringBuilder sb = new StringBuilder();

            for (int i = 0; i < idt; i++) {
                sb.append("\t");
            }

            return sb.toString();
        }
    }
}