From 119d6fceef071b2fab676a0c7c9e3bc0c045379b Mon Sep 17 00:00:00 2001 From: Akos Kitta Date: Thu, 24 Jan 2019 16:02:26 +0100 Subject: [PATCH] GH-508: Copied o.e.j.i.corext.callhierarchy source from JDT UI to LS. Signed-off-by: Akos Kitta --- .../corext/callhierarchy/CallHierarchy.java | 316 +++++++++++++++ .../callhierarchy/CallHierarchyMessages.java | 29 ++ .../CallHierarchyMessages.properties | 13 + .../callhierarchy/CallHierarchyVisitor.java | 24 ++ .../corext/callhierarchy/CallLocation.java | 134 +++++++ .../CallSearchResultCollector.java | 81 ++++ .../callhierarchy/CalleeAnalyzerVisitor.java | 351 +++++++++++++++++ .../callhierarchy/CalleeMethodWrapper.java | 102 +++++ .../callhierarchy/CallerMethodWrapper.java | 213 ++++++++++ .../callhierarchy/IImplementorFinder.java | 31 ++ .../corext/callhierarchy/Implementors.java | 194 +++++++++ .../callhierarchy/JavaImplementorFinder.java | 60 +++ .../corext/callhierarchy/MethodCall.java | 74 ++++ .../MethodReferencesSearchRequestor.java | 55 +++ .../corext/callhierarchy/MethodWrapper.java | 372 ++++++++++++++++++ .../corext/callhierarchy/RealCallers.java | 39 ++ 16 files changed, 2088 insertions(+) create mode 100644 org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/corext/callhierarchy/CallHierarchy.java create mode 100644 org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/corext/callhierarchy/CallHierarchyMessages.java create mode 100644 org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/corext/callhierarchy/CallHierarchyMessages.properties create mode 100644 org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/corext/callhierarchy/CallHierarchyVisitor.java create mode 100644 org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/corext/callhierarchy/CallLocation.java create mode 100644 org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/corext/callhierarchy/CallSearchResultCollector.java create mode 100644 org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/corext/callhierarchy/CalleeAnalyzerVisitor.java create mode 100644 org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/corext/callhierarchy/CalleeMethodWrapper.java create mode 100644 org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/corext/callhierarchy/CallerMethodWrapper.java create mode 100644 org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/corext/callhierarchy/IImplementorFinder.java create mode 100644 org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/corext/callhierarchy/Implementors.java create mode 100644 org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/corext/callhierarchy/JavaImplementorFinder.java create mode 100644 org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/corext/callhierarchy/MethodCall.java create mode 100644 org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/corext/callhierarchy/MethodReferencesSearchRequestor.java create mode 100644 org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/corext/callhierarchy/MethodWrapper.java create mode 100644 org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/corext/callhierarchy/RealCallers.java diff --git a/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/corext/callhierarchy/CallHierarchy.java b/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/corext/callhierarchy/CallHierarchy.java new file mode 100644 index 0000000000..fc2b5c591b --- /dev/null +++ b/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/corext/callhierarchy/CallHierarchy.java @@ -0,0 +1,316 @@ +/******************************************************************************* + * Copyright (c) 2000, 2017 IBM Corporation and others. + * 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: + * Jesper Kamstrup Linnet (eclipse@kamstrup-linnet.dk) - initial API and implementation + * (report 36180: Callers/Callees view) + * Stephan Herrmann (stephan@cs.tu-berlin.de): + * - bug 75800: [call hierarchy] should allow searches for fields + *******************************************************************************/ +package org.eclipse.jdt.ls.core.internal.corext.callhierarchy; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Iterator; +import java.util.List; +import java.util.StringTokenizer; + +import org.eclipse.core.runtime.NullProgressMonitor; +import org.eclipse.jdt.core.IJavaElement; +import org.eclipse.jdt.core.IMember; +import org.eclipse.jdt.core.IMethod; +import org.eclipse.jdt.core.IModuleDescription; +import org.eclipse.jdt.core.IType; +import org.eclipse.jdt.core.ITypeRoot; +import org.eclipse.jdt.core.JavaModelException; +import org.eclipse.jdt.core.dom.ASTParser; +import org.eclipse.jdt.core.dom.CompilationUnit; +import org.eclipse.jdt.core.search.IJavaSearchScope; +import org.eclipse.jdt.core.search.SearchEngine; +import org.eclipse.jdt.internal.corext.dom.IASTSharedValues; +import org.eclipse.jdt.internal.corext.refactoring.util.JavaElementUtil; +import org.eclipse.jdt.internal.ui.JavaPlugin; +import org.eclipse.jface.preference.IPreferenceStore; + +public class CallHierarchy { + private static final String PREF_USE_IMPLEMENTORS= "PREF_USE_IMPLEMENTORS"; //$NON-NLS-1$ + private static final String PREF_USE_FILTERS = "PREF_USE_FILTERS"; //$NON-NLS-1$ + private static final String PREF_FILTERS_LIST = "PREF_FILTERS_LIST"; //$NON-NLS-1$ + + private static final String DEFAULT_IGNORE_FILTERS = "java.*,javax.*"; //$NON-NLS-1$ + private static CallHierarchy fgInstance; + private IJavaSearchScope fSearchScope; + private StringMatcher[] fFilters; + + public static CallHierarchy getDefault() { + if (fgInstance == null) { + fgInstance = new CallHierarchy(); + } + + return fgInstance; + } + + public boolean isSearchUsingImplementorsEnabled() { + IPreferenceStore settings = JavaPlugin.getDefault().getPreferenceStore(); + + return settings.getBoolean(PREF_USE_IMPLEMENTORS); + } + + public void setSearchUsingImplementorsEnabled(boolean enabled) { + IPreferenceStore settings = JavaPlugin.getDefault().getPreferenceStore(); + + settings.setValue(PREF_USE_IMPLEMENTORS, enabled); + } + + public Collection getImplementingMethods(IMethod method) { + if (isSearchUsingImplementorsEnabled()) { + IJavaElement[] result = Implementors.getInstance().searchForImplementors(new IJavaElement[] { + method + }, new NullProgressMonitor()); + + if ((result != null) && (result.length > 0)) { + return Arrays.asList(result); + } + } + + return new ArrayList<>(0); + } + + public Collection getInterfaceMethods(IMethod method) { + if (isSearchUsingImplementorsEnabled()) { + IJavaElement[] result = Implementors.getInstance().searchForInterfaces(new IJavaElement[] { + method + }, new NullProgressMonitor()); + + if ((result != null) && (result.length > 0)) { + return Arrays.asList(result); + } + } + + return new ArrayList<>(0); + } + + public MethodWrapper[] getCallerRoots(IMember[] members) { + return getRoots(members, true); + } + + public MethodWrapper[] getCalleeRoots(IMember[] members) { + return getRoots(members, false); + } + + private MethodWrapper[] getRoots(IMember[] members, boolean callers) { + ArrayList roots= new ArrayList<>(); + for (int i= 0; i < members.length; i++) { + IMember member= members[i]; + if (member instanceof IType) { + IType type= (IType) member; + try { + if (! type.isAnonymous()) { + IMethod[] constructors= JavaElementUtil.getAllConstructors(type); + if (constructors.length == 0) { + addRoot(member, roots, callers); // IType is a stand-in for the non-existing default constructor + } else { + for (int j= 0; j < constructors.length; j++) { + IMethod constructor= constructors[j]; + addRoot(constructor, roots, callers); + } + } + } else { + addRoot(member, roots, callers); + } + } catch (JavaModelException e) { + JavaPlugin.log(e); + } + } else { + addRoot(member, roots, callers); + } + } + return roots.toArray(new MethodWrapper[roots.size()]); + } + + private void addRoot(IMember member, ArrayList roots, boolean callers) { + MethodCall methodCall= new MethodCall(member); + MethodWrapper root; + if (callers) { + root= new CallerMethodWrapper(null, methodCall); + } else { + root= new CalleeMethodWrapper(null, methodCall); + } + roots.add(root); + } + + public static CallLocation getCallLocation(Object element) { + CallLocation callLocation = null; + + if (element instanceof MethodWrapper) { + MethodWrapper methodWrapper = (MethodWrapper) element; + MethodCall methodCall = methodWrapper.getMethodCall(); + + if (methodCall != null) { + callLocation = methodCall.getFirstCallLocation(); + } + } else if (element instanceof CallLocation) { + callLocation = (CallLocation) element; + } + + return callLocation; + } + + public IJavaSearchScope getSearchScope() { + if (fSearchScope == null) { + fSearchScope= SearchEngine.createWorkspaceScope(); + } + + return fSearchScope; + } + + public void setSearchScope(IJavaSearchScope searchScope) { + this.fSearchScope = searchScope; + } + + /** + * Checks whether the fully qualified name is ignored by the set filters. + * + * @param fullyQualifiedName the fully qualified name + * + * @return true if the fully qualified name is ignored + */ + public boolean isIgnored(String fullyQualifiedName) { + if ((getIgnoreFilters() != null) && (getIgnoreFilters().length > 0)) { + for (int i = 0; i < getIgnoreFilters().length; i++) { + String fullyQualifiedName1 = fullyQualifiedName; + + if (getIgnoreFilters()[i].match(fullyQualifiedName1)) { + return true; + } + } + } + + return false; + } + + public boolean isFilterEnabled() { + IPreferenceStore settings = JavaPlugin.getDefault().getPreferenceStore(); + return settings.getBoolean(PREF_USE_FILTERS); + } + + public void setFilterEnabled(boolean filterEnabled) { + IPreferenceStore settings = JavaPlugin.getDefault().getPreferenceStore(); + settings.setValue(PREF_USE_FILTERS, filterEnabled); + } + + /** + * Returns the current filters as a string. + * @return returns the filters + */ + public String getFilters() { + IPreferenceStore settings = JavaPlugin.getDefault().getPreferenceStore(); + + return settings.getString(PREF_FILTERS_LIST); + } + + public void setFilters(String filters) { + fFilters = null; + + IPreferenceStore settings = JavaPlugin.getDefault().getPreferenceStore(); + settings.setValue(PREF_FILTERS_LIST, filters); + } + + /** + * Returns filters for packages which should not be included in the search results. + * + * @return StringMatcher[] + */ + private StringMatcher[] getIgnoreFilters() { + if (fFilters == null) { + String filterString = null; + + if (isFilterEnabled()) { + filterString = getFilters(); + + if (filterString == null) { + filterString = DEFAULT_IGNORE_FILTERS; + } + } + + if (filterString != null) { + fFilters = parseList(filterString); + } else { + fFilters = null; + } + } + + return fFilters; + } + + public static boolean arePossibleInputElements(List elements) { + if (elements.size() < 1) { + return false; + } + for (Iterator iter= elements.iterator(); iter.hasNext();) { + if (! isPossibleInputElement(iter.next())) { + return false; + } + } + return true; + } + + /** + * Parses the comma separated string into an array of {@link StringMatcher} objects. + * + * @param listString the string to parse + * @return an array of {@link StringMatcher} objects + */ + private static StringMatcher[] parseList(String listString) { + List list = new ArrayList<>(10); + StringTokenizer tokenizer = new StringTokenizer(listString, ","); //$NON-NLS-1$ + + while (tokenizer.hasMoreTokens()) { + String textFilter = tokenizer.nextToken().trim(); + list.add(new StringMatcher(textFilter, false, false)); + } + + return list.toArray(new StringMatcher[list.size()]); + } + + static CompilationUnit getCompilationUnitNode(IMember member, boolean resolveBindings) { + ITypeRoot typeRoot= member.getTypeRoot(); + try { + if (typeRoot.exists() && typeRoot.getBuffer() != null) { + ASTParser parser= ASTParser.newParser(IASTSharedValues.SHARED_AST_LEVEL); + parser.setSource(typeRoot); + parser.setResolveBindings(resolveBindings); + return (CompilationUnit) parser.createAST(null); + } + } catch (JavaModelException e) { + JavaPlugin.log(e); + } + return null; + } + + public static boolean isPossibleInputElement(Object element){ + if (! (element instanceof IMember)) { + return false; + } + + if (element instanceof IModuleDescription) { + return false; + } + + if (element instanceof IType) { + IType type= (IType) element; + try { + return type.isClass() || type.isEnum(); + } catch (JavaModelException e) { + return false; + } + } + + return true; + } +} diff --git a/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/corext/callhierarchy/CallHierarchyMessages.java b/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/corext/callhierarchy/CallHierarchyMessages.java new file mode 100644 index 0000000000..754018deed --- /dev/null +++ b/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/corext/callhierarchy/CallHierarchyMessages.java @@ -0,0 +1,29 @@ +/******************************************************************************* + * Copyright (c) 2000, 2005 IBM Corporation and others. + * 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: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +package org.eclipse.jdt.ls.core.internal.corext.callhierarchy; + +import org.eclipse.osgi.util.NLS; + +public final class CallHierarchyMessages extends NLS { + + private static final String BUNDLE_NAME= "org.eclipse.jdt.internal.corext.callhierarchy.CallHierarchyMessages";//$NON-NLS-1$ + + private CallHierarchyMessages() { + // Do not instantiate + } + + public static String CallerMethodWrapper_taskname; + public static String CalleeMethodWrapper_taskname; + + static { + NLS.initializeMessages(BUNDLE_NAME, CallHierarchyMessages.class); + } +} \ No newline at end of file diff --git a/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/corext/callhierarchy/CallHierarchyMessages.properties b/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/corext/callhierarchy/CallHierarchyMessages.properties new file mode 100644 index 0000000000..adfd8c3efe --- /dev/null +++ b/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/corext/callhierarchy/CallHierarchyMessages.properties @@ -0,0 +1,13 @@ +############################################################################### +# Copyright (c) 2000, 2005 IBM Corporation and others. +# 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: +# Jesper Kamstrup Linnet (eclipse@kamstrup-linnet.dk) - initial API and implementation +# (report 36180: Callers/Callees view) +############################################################################### +CallerMethodWrapper_taskname=Finding callers... +CalleeMethodWrapper_taskname=Finding callees... diff --git a/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/corext/callhierarchy/CallHierarchyVisitor.java b/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/corext/callhierarchy/CallHierarchyVisitor.java new file mode 100644 index 0000000000..c5d561ae95 --- /dev/null +++ b/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/corext/callhierarchy/CallHierarchyVisitor.java @@ -0,0 +1,24 @@ +/******************************************************************************* + * Copyright (c) 2000, 2008 IBM Corporation and others. + * 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: + * Jesper Kamstrup Linnet (eclipse@kamstrup-linnet.dk) - initial API and implementation + * (report 36180: Callers/Callees view) + *******************************************************************************/ +package org.eclipse.jdt.ls.core.internal.corext.callhierarchy; + +public abstract class CallHierarchyVisitor { + public void preVisit(MethodWrapper methodWrapper) { + } + + public void postVisit(MethodWrapper methodWrapper) { + } + + public boolean visit(MethodWrapper methodWrapper) { + return true; + } +} diff --git a/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/corext/callhierarchy/CallLocation.java b/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/corext/callhierarchy/CallLocation.java new file mode 100644 index 0000000000..ba6d2f8e30 --- /dev/null +++ b/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/corext/callhierarchy/CallLocation.java @@ -0,0 +1,134 @@ +/******************************************************************************* + * Copyright (c) 2000, 2015 IBM Corporation and others. + * 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: + * Jesper Kamstrup Linnet (eclipse@kamstrup-linnet.dk) - initial API and implementation + * (report 36180: Callers/Callees view) + *******************************************************************************/ +package org.eclipse.jdt.ls.core.internal.corext.callhierarchy; + +import org.eclipse.core.runtime.IAdaptable; +import org.eclipse.jdt.core.IBuffer; +import org.eclipse.jdt.core.IJavaElement; +import org.eclipse.jdt.core.IMember; +import org.eclipse.jdt.core.IOpenable; +import org.eclipse.jdt.core.JavaModelException; +import org.eclipse.jdt.internal.ui.JavaPlugin; +import org.eclipse.jface.text.BadLocationException; +import org.eclipse.jface.text.Document; + +public class CallLocation implements IAdaptable { + public static final int UNKNOWN_LINE_NUMBER= -1; + private IMember fMember; + private IMember fCalledMember; + private int fStart; + private int fEnd; + + private String fCallText; + private int fLineNumber; + + public CallLocation(IMember member, IMember calledMember, int start, int end, int lineNumber) { + this.fMember = member; + this.fCalledMember = calledMember; + this.fStart = start; + this.fEnd = end; + this.fLineNumber= lineNumber; + } + + /** + * @return IMethod + */ + public IMember getCalledMember() { + return fCalledMember; + } + + /** + * + */ + public int getEnd() { + return fEnd; + } + + public IMember getMember() { + return fMember; + } + + /** + * + */ + public int getStart() { + return fStart; + } + + public int getLineNumber() { + initCallTextAndLineNumber(); + return fLineNumber; + } + + public String getCallText() { + initCallTextAndLineNumber(); + return fCallText; + } + + private void initCallTextAndLineNumber() { + if (fCallText != null) { + return; + } + + IBuffer buffer= getBufferForMember(); + if (buffer == null || buffer.getLength() < fEnd) { //binary, without source attachment || buffer contents out of sync (bug 121900) + fCallText= ""; //$NON-NLS-1$ + fLineNumber= UNKNOWN_LINE_NUMBER; + return; + } + + fCallText= buffer.getText(fStart, (fEnd - fStart)); + + if (fLineNumber == UNKNOWN_LINE_NUMBER) { + Document document= new Document(buffer.getContents()); + try { + fLineNumber= document.getLineOfOffset(fStart) + 1; + } catch (BadLocationException e) { + JavaPlugin.log(e); + } + } + } + + /** + * Returns the IBuffer for the IMember represented by this CallLocation. + * + * @return IBuffer for the IMember or null if the member doesn't have a buffer (for + * example if it is a binary file without source attachment). + */ + private IBuffer getBufferForMember() { + IBuffer buffer = null; + try { + IOpenable openable = fMember.getOpenable(); + if (openable != null && fMember.exists()) { + buffer = openable.getBuffer(); + } + } catch (JavaModelException e) { + JavaPlugin.log(e); + } + return buffer; + } + + @Override + public String toString() { + return getCallText(); + } + + @Override + @SuppressWarnings("unchecked") + public T getAdapter(Class adapter) { + if (IJavaElement.class.isAssignableFrom(adapter)) { + return (T) getMember(); + } + + return null; + } +} diff --git a/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/corext/callhierarchy/CallSearchResultCollector.java b/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/corext/callhierarchy/CallSearchResultCollector.java new file mode 100644 index 0000000000..b808b55961 --- /dev/null +++ b/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/corext/callhierarchy/CallSearchResultCollector.java @@ -0,0 +1,81 @@ +/******************************************************************************* + * Copyright (c) 2000, 2011 IBM Corporation and others. + * 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: + * Jesper Kamstrup Linnet (eclipse@kamstrup-linnet.dk) - initial API and implementation + * (report 36180: Callers/Callees view) + *******************************************************************************/ +package org.eclipse.jdt.ls.core.internal.corext.callhierarchy; + +import java.util.HashMap; +import java.util.Map; + +import org.eclipse.jdt.core.IJavaElement; +import org.eclipse.jdt.core.IMember; +import org.eclipse.jdt.core.IType; + +class CallSearchResultCollector { + /** + * A map from handle identifier ({@link String}) to {@link MethodCall}. + */ + private Map fCalledMembers; + + public CallSearchResultCollector() { + this.fCalledMembers = createCalledMethodsData(); + } + + /** + * @return a map from handle identifier ({@link String}) to {@link MethodCall} + */ + public Map getCallers() { + return fCalledMembers; + } + + protected void addMember(IMember member, IMember calledMember, int start, int end) { + addMember(member, calledMember, start, end, CallLocation.UNKNOWN_LINE_NUMBER); + } + + protected void addMember(IMember member, IMember calledMember, int start, int end, int lineNumber) { + if ((member != null) && (calledMember != null)) { + if (!isIgnored(calledMember)) { + MethodCall methodCall = fCalledMembers.get(calledMember.getHandleIdentifier()); + + if (methodCall == null) { + methodCall = new MethodCall(calledMember); + fCalledMembers.put(calledMember.getHandleIdentifier(), methodCall); + } + + methodCall.addCallLocation(new CallLocation(member, calledMember, start, + end, lineNumber)); + } + } + } + + protected Map createCalledMethodsData() { + return new HashMap<>(); + } + + /** + * Method isIgnored. + * @param enclosingElement + * @return boolean + */ + private boolean isIgnored(IMember enclosingElement) { + String fullyQualifiedName = getTypeOfElement(enclosingElement) + .getFullyQualifiedName(); + + return CallHierarchy.getDefault().isIgnored(fullyQualifiedName); + } + + private IType getTypeOfElement(IMember element) { + if (element.getElementType() == IJavaElement.TYPE) { + return (IType) element; + } + + return element.getDeclaringType(); + } +} diff --git a/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/corext/callhierarchy/CalleeAnalyzerVisitor.java b/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/corext/callhierarchy/CalleeAnalyzerVisitor.java new file mode 100644 index 0000000000..7f8b735f06 --- /dev/null +++ b/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/corext/callhierarchy/CalleeAnalyzerVisitor.java @@ -0,0 +1,351 @@ +/******************************************************************************* + * Copyright (c) 2000, 2014 IBM Corporation and others. + * 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: + * Jesper Kamstrup Linnet (eclipse@kamstrup-linnet.dk) - initial API and implementation + * (report 36180: Callers/Callees view) + *******************************************************************************/ +package org.eclipse.jdt.ls.core.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.corext.dom.Bindings; +import org.eclipse.jdt.internal.corext.dom.HierarchicalASTVisitor; +import org.eclipse.jdt.internal.corext.util.JavaModelUtil; +import org.eclipse.jdt.internal.ui.JavaPlugin; + +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) { + JavaPlugin.log(jme); + } + } + + /** + * @return a map from handle identifier ({@link String}) to {@link MethodCall} + */ + public Map 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; + } + + /** + * Find all constructor invocations (this(...)) from the called method. + * Since we only traverse into the AST on the wanted method declaration, this method + * should not hit on more constructor invocations than those in the wanted method. + * + * @see org.eclipse.jdt.core.dom.ASTVisitor#visit(org.eclipse.jdt.core.dom.ConstructorInvocation) + */ + @Override + public boolean visit(ConstructorInvocation node) { + progressMonitorWorked(1); + if (!isFurtherTraversalNecessary(node)) { + return false; + } + + if (isNodeWithinMethod(node)) { + addMethodCall(node.resolveConstructorBinding(), node); + } + + return true; + } + + /** + * @see HierarchicalASTVisitor#visit(org.eclipse.jdt.core.dom.AbstractTypeDeclaration) + */ + @Override + public boolean visit(AbstractTypeDeclaration node) { + progressMonitorWorked(1); + if (!isFurtherTraversalNecessary(node)) { + return false; + } + + if (isNodeWithinMethod(node)) { + List bodyDeclarations= node.bodyDeclarations(); + for (Iterator 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; + } + + /** + * @see org.eclipse.jdt.core.dom.ASTVisitor#visit(org.eclipse.jdt.core.dom.MethodDeclaration) + */ + @Override + public boolean visit(MethodDeclaration node) { + progressMonitorWorked(1); + return isFurtherTraversalNecessary(node); + } + + /** + * Find all method invocations from the called method. Since we only traverse into + * the AST on the wanted method declaration, this method should not hit on more + * method invocations than those in the wanted method. + * + * @see org.eclipse.jdt.core.dom.ASTVisitor#visit(org.eclipse.jdt.core.dom.MethodInvocation) + */ + @Override + public boolean visit(MethodInvocation node) { + progressMonitorWorked(1); + if (!isFurtherTraversalNecessary(node)) { + return false; + } + + if (isNodeWithinMethod(node)) { + addMethodCall(node.resolveMethodBinding(), node); + } + + return true; + } + + /** + * Find invocations of the supertype's constructor from the called method + * (=constructor). Since we only traverse into the AST on the wanted method + * declaration, this method should not hit on more method invocations than those in + * the wanted method. + * + * @see org.eclipse.jdt.core.dom.ASTVisitor#visit(org.eclipse.jdt.core.dom.SuperConstructorInvocation) + */ + @Override + public boolean visit(SuperConstructorInvocation node) { + progressMonitorWorked(1); + if (!isFurtherTraversalNecessary(node)) { + return false; + } + + if (isNodeWithinMethod(node)) { + addMethodCall(node.resolveConstructorBinding(), node); + } + + return true; + } + + /** + * Find all method invocations from the called method. Since we only traverse into + * the AST on the wanted method declaration, this method should not hit on more + * method invocations than those in the wanted method. + * @param node node to visit + * @return whether children should be visited + * + * @see org.eclipse.jdt.core.dom.ASTVisitor#visit(org.eclipse.jdt.core.dom.MethodInvocation) + */ + @Override + public boolean visit(SuperMethodInvocation node) { + progressMonitorWorked(1); + if (!isFurtherTraversalNecessary(node)) { + return false; + } + + if (isNodeWithinMethod(node)) { + addMethodCall(node.resolveMethodBinding(), node); + } + + return true; + } + + /** + * When an anonymous class declaration is reached, the traversal should not go further since it's not + * supposed to consider calls inside the anonymous inner class as calls from the outer method. + * + * @see org.eclipse.jdt.core.dom.ASTVisitor#visit(org.eclipse.jdt.core.dom.AnonymousClassDeclaration) + */ + @Override + public boolean visit(AnonymousClassDeclaration node) { + return isNodeEnclosingMethod(node); + } + + + /** + * Adds the specified method binding to the search results. + * + * @param calledMethodBinding the called method binding + * @param node the AST 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())) { //$NON-NLS-1$ + 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) { + JavaPlugin.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 CallHierarchy.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) { + // Is the method completely enclosed by the node? + return true; + } + return false; + } + + private boolean isFurtherTraversalNecessary(ASTNode node) { + return isNodeWithinMethod(node) || isNodeEnclosingMethod(node); + } + + private IMethod findImplementingMethods(IMethod calledMethod) { + Collection implementingMethods = CallHierarchy.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(); + } + } + } +} diff --git a/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/corext/callhierarchy/CalleeMethodWrapper.java b/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/corext/callhierarchy/CalleeMethodWrapper.java new file mode 100644 index 0000000000..046cef5dca --- /dev/null +++ b/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/corext/callhierarchy/CalleeMethodWrapper.java @@ -0,0 +1,102 @@ +/******************************************************************************* + * Copyright (c) 2000, 2011 IBM Corporation and others. + * 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: + * Jesper Kamstrup Linnet (eclipse@kamstrup-linnet.dk) - initial API and implementation + * (report 36180: Callers/Callees view) + *******************************************************************************/ +package org.eclipse.jdt.ls.core.internal.corext.callhierarchy; + +import java.util.Arrays; +import java.util.Comparator; +import java.util.HashMap; +import java.util.Map; + +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.jdt.core.IMember; +import org.eclipse.jdt.core.dom.CompilationUnit; + +class CalleeMethodWrapper extends MethodWrapper { + private Comparator fMethodWrapperComparator = new MethodWrapperComparator(); + + private static class MethodWrapperComparator implements Comparator { + @Override + public int compare(MethodWrapper m1, MethodWrapper m2) { + CallLocation callLocation1 = m1.getMethodCall().getFirstCallLocation(); + CallLocation callLocation2 = m2.getMethodCall().getFirstCallLocation(); + + if ((callLocation1 != null) && (callLocation2 != null)) { + if (callLocation1.getStart() == callLocation2.getStart()) { + return callLocation1.getEnd() - callLocation2.getEnd(); + } + + return callLocation1.getStart() - callLocation2.getStart(); + } + + return 0; + } + } + + public CalleeMethodWrapper(MethodWrapper parent, MethodCall methodCall) { + super(parent, methodCall); + } + + /* Returns the calls sorted after the call location + * @see org.eclipse.jdt.internal.corext.callhierarchy.MethodWrapper#getCalls() + */ + @Override + public MethodWrapper[] getCalls(IProgressMonitor progressMonitor) { + MethodWrapper[] result = super.getCalls(progressMonitor); + Arrays.sort(result, fMethodWrapperComparator); + + return result; + } + + @Override + protected String getTaskName() { + return CallHierarchyMessages.CalleeMethodWrapper_taskname; + } + + /* + * @see org.eclipse.jdt.internal.corext.callhierarchy.MethodWrapper#createMethodWrapper(org.eclipse.jdt.internal.corext.callhierarchy.MethodCall) + */ + @Override + protected MethodWrapper createMethodWrapper(MethodCall methodCall) { + return new CalleeMethodWrapper(this, methodCall); + } + + /* + * @see org.eclipse.jdt.internal.corext.callhierarchy.MethodWrapper#canHaveChildren() + */ + @Override + public boolean canHaveChildren() { + return true; + } + + /** + * Find callees called from the current method. + * @see org.eclipse.jdt.internal.corext.callhierarchy.MethodWrapper#findChildren(org.eclipse.core.runtime.IProgressMonitor) + */ + @Override + protected Map findChildren(IProgressMonitor progressMonitor) { + IMember member= getMember(); + if (member.exists()) { + CompilationUnit cu= CallHierarchy.getCompilationUnitNode(member, true); + if (progressMonitor != null) { + progressMonitor.worked(5); + } + + if (cu != null) { + CalleeAnalyzerVisitor visitor = new CalleeAnalyzerVisitor(member, cu, progressMonitor); + + cu.accept(visitor); + return visitor.getCallees(); + } + } + return new HashMap<>(0); + } +} diff --git a/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/corext/callhierarchy/CallerMethodWrapper.java b/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/corext/callhierarchy/CallerMethodWrapper.java new file mode 100644 index 0000000000..4fc23ead36 --- /dev/null +++ b/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/corext/callhierarchy/CallerMethodWrapper.java @@ -0,0 +1,213 @@ +/******************************************************************************* + * Copyright (c) 2000, 2011 IBM Corporation and others. + * 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: + * Jesper Kamstrup Linnet (eclipse@kamstrup-linnet.dk) - initial API and implementation + * (report 36180: Callers/Callees view) + * Stephan Herrmann (stephan@cs.tu-berlin.de): + * - bug 206949: [call hierarchy] filter field accesses (only write or only read) + *******************************************************************************/ +package org.eclipse.jdt.ls.core.internal.corext.callhierarchy; + +import java.util.HashMap; +import java.util.Map; + +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.core.runtime.SubProgressMonitor; +import org.eclipse.jdt.core.Flags; +import org.eclipse.jdt.core.IField; +import org.eclipse.jdt.core.IInitializer; +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.search.IJavaSearchConstants; +import org.eclipse.jdt.core.search.IJavaSearchScope; +import org.eclipse.jdt.core.search.SearchEngine; +import org.eclipse.jdt.core.search.SearchParticipant; +import org.eclipse.jdt.core.search.SearchPattern; +import org.eclipse.jdt.internal.corext.util.JdtFlags; +import org.eclipse.jdt.internal.corext.util.SearchUtils; +import org.eclipse.jdt.internal.ui.JavaPlugin; + +public class CallerMethodWrapper extends MethodWrapper { + /** + * Value of the expand with constructors mode. + * + * @since 3.5 + */ + private boolean fExpandWithConstructors; + + /** + * Tells whether the expand with constructors mode has been set. + * + * @see #setExpandWithConstructors(boolean) + * @since 3.5 + */ + private boolean fIsExpandWithConstructorsSet; + + public CallerMethodWrapper(MethodWrapper parent, MethodCall methodCall) { + super(parent, methodCall); + } + + protected IJavaSearchScope getSearchScope() { + return CallHierarchy.getDefault().getSearchScope(); + } + + @Override + protected String getTaskName() { + return CallHierarchyMessages.CallerMethodWrapper_taskname; + } + + @Override + public MethodWrapper createMethodWrapper(MethodCall methodCall) { + return new CallerMethodWrapper(this, methodCall); + } + + /* + * @see org.eclipse.jdt.internal.corext.callhierarchy.MethodWrapper#canHaveChildren() + */ + @Override + public boolean canHaveChildren() { + IMember member= getMember(); + if (member instanceof IField) { + if (getLevel() == 1) { + return true; + } + int mode= getFieldSearchMode(); + return mode == IJavaSearchConstants.REFERENCES || mode == IJavaSearchConstants.READ_ACCESSES; + } + return member instanceof IMethod || member instanceof IType; + } + + /** + * @return The result of the search for children + * @see org.eclipse.jdt.internal.corext.callhierarchy.MethodWrapper#findChildren(org.eclipse.core.runtime.IProgressMonitor) + */ + @Override + protected Map findChildren(IProgressMonitor progressMonitor) { + try { + + IProgressMonitor monitor= new SubProgressMonitor(progressMonitor, 95, SubProgressMonitor.SUPPRESS_SUBTASK_LABEL); + + checkCanceled(progressMonitor); + + IMember member= getMember(); + SearchPattern pattern= null; + IType type= null; + if (member instanceof IType) { + type= (IType) member; + } else if (member instanceof IInitializer && ! Flags.isStatic(member.getFlags())) { + type= (IType) member.getParent(); + } + if (type != null) { + if (type.isAnonymous()) { + // search engine does not find reference to anonymous, see https://bugs.eclipse.org/bugs/show_bug.cgi?id=207774 + CallSearchResultCollector resultCollector= new CallSearchResultCollector(); + IJavaElement parent= type.getParent(); + if (parent instanceof IMember) { + IMember parentMember= (IMember) parent; + ISourceRange nameRange= type.getNameRange(); + int start= nameRange != null ? nameRange.getOffset() : -1; + int len= nameRange != null ? nameRange.getLength() : 0; + resultCollector.addMember(type, parentMember, start, start + len); + return resultCollector.getCallers(); + } + } else if (type.getParent() instanceof IMethod) { + // good enough for local types (does not find super(..) references in subtype constructors): + pattern= SearchPattern.createPattern(type, + IJavaSearchConstants.CLASS_INSTANCE_CREATION_TYPE_REFERENCE, + SearchUtils.GENERICS_AGNOSTIC_MATCH_RULE); + } else { + pattern= SearchPattern.createPattern(type.getFullyQualifiedName('.'), + IJavaSearchConstants.CONSTRUCTOR, + IJavaSearchConstants.REFERENCES, + SearchUtils.GENERICS_AGNOSTIC_MATCH_RULE); + } + } + if (pattern == null) { + int limitTo= IJavaSearchConstants.REFERENCES; + if (member.getElementType() == IJavaElement.FIELD) { + limitTo= getFieldSearchMode(); + } + pattern= SearchPattern.createPattern(member, limitTo, SearchUtils.GENERICS_AGNOSTIC_MATCH_RULE); + } + if (pattern == null) { // e.g. for initializers + return new HashMap<>(0); + } + + SearchEngine searchEngine= new SearchEngine(); + MethodReferencesSearchRequestor searchRequestor= new MethodReferencesSearchRequestor(); + IJavaSearchScope defaultSearchScope= getSearchScope(); + boolean isWorkspaceScope= SearchEngine.createWorkspaceScope().equals(defaultSearchScope); + IJavaSearchScope searchScope= isWorkspaceScope ? getAccurateSearchScope(defaultSearchScope, member) : defaultSearchScope; + searchEngine.search(pattern, new SearchParticipant[] { SearchEngine.getDefaultSearchParticipant() }, searchScope, searchRequestor, + monitor); + return searchRequestor.getCallers(); + + } catch (CoreException e) { + JavaPlugin.log(e); + return new HashMap<>(0); + } + } + + private IJavaSearchScope getAccurateSearchScope(IJavaSearchScope defaultSearchScope, IMember member) throws JavaModelException { + if (! JdtFlags.isPrivate(member)) { + return defaultSearchScope; + } + + if (member.getCompilationUnit() != null) { + return SearchEngine.createJavaSearchScope(new IJavaElement[] { member.getCompilationUnit() }); + } else if (member.getClassFile() != null) { + // member could be called from an inner class-> search + // package fragment (see also bug 109053): + return SearchEngine.createJavaSearchScope(new IJavaElement[] { member.getAncestor(IJavaElement.PACKAGE_FRAGMENT) }); + } else { + return defaultSearchScope; + } + } + + /** + * Returns the value of expand with constructors mode. + * + * @return true if in expand with constructors mode, false otherwise or if not yet set + * @see #isExpandWithConstructorsSet() + * + * @since 3.5 + */ + public boolean getExpandWithConstructors() { + return fIsExpandWithConstructorsSet && fExpandWithConstructors; + } + + /** + * Sets the expand with constructors mode. + * + * @param value true if in expand with constructors mode, false + * otherwise + * @since 3.5 + */ + public void setExpandWithConstructors(boolean value) { + fExpandWithConstructors= value; + fIsExpandWithConstructorsSet= true; + + } + + /** + * Tells whether the expand with constructors mode has been set. + * + * @return true if expand with constructors mode has been set explicitly, false otherwise + * @see #setExpandWithConstructors(boolean) + * @since 3.5 + */ + public boolean isExpandWithConstructorsSet() { + return fIsExpandWithConstructorsSet; + } + +} diff --git a/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/corext/callhierarchy/IImplementorFinder.java b/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/corext/callhierarchy/IImplementorFinder.java new file mode 100644 index 0000000000..8011e4662e --- /dev/null +++ b/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/corext/callhierarchy/IImplementorFinder.java @@ -0,0 +1,31 @@ +/******************************************************************************* + * Copyright (c) 2000, 2011 IBM Corporation and others. + * 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: + * Jesper Kamstrup Linnet (eclipse@kamstrup-linnet.dk) - initial API and implementation + * (report 36180: Callers/Callees view) + *******************************************************************************/ +package org.eclipse.jdt.ls.core.internal.corext.callhierarchy; + +import java.util.Collection; + +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.jdt.core.IType; + +public interface IImplementorFinder { + + /** + * Find implementors of the specified IType instance. + */ + public abstract Collection findImplementingTypes(IType type, + IProgressMonitor progressMonitor); + + /** + * Find interfaces which are implemented by the specified IType instance. + */ + public abstract Collection findInterfaces(IType type, IProgressMonitor progressMonitor); +} diff --git a/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/corext/callhierarchy/Implementors.java b/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/corext/callhierarchy/Implementors.java new file mode 100644 index 0000000000..c4845ab4c6 --- /dev/null +++ b/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/corext/callhierarchy/Implementors.java @@ -0,0 +1,194 @@ +/******************************************************************************* + * Copyright (c) 2000, 2011 IBM Corporation and others. + * 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: + * Jesper Kamstrup Linnet (eclipse@kamstrup-linnet.dk) - initial API and implementation + * (report 36180: Callers/Callees view) + *******************************************************************************/ +package org.eclipse.jdt.ls.core.internal.corext.callhierarchy; + +import java.util.ArrayList; +import java.util.Collection; + +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.core.runtime.SubProgressMonitor; +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.JavaModelException; +import org.eclipse.jdt.internal.ui.JavaPlugin; + +/** + * The main plugin class to be used in the desktop. + */ +public class Implementors { + private static IImplementorFinder[] IMPLEMENTOR_FINDERS= new IImplementorFinder[] { new JavaImplementorFinder() }; + private static Implementors fgInstance; + + /** + * Returns the shared instance. + */ + public static Implementors getInstance() { + if (fgInstance == null) { + fgInstance = new Implementors(); + } + + return fgInstance; + } + + /** + * Searches for implementors of the specified Java elements. Currently, only IMethod + * instances are searched for. Also, only the first element of the elements + * parameter is taken into consideration. + * + * @param elements + * + * @return An array of found implementing Java elements (currently only IMethod + * instances) + */ + public IJavaElement[] searchForImplementors(IJavaElement[] elements, + IProgressMonitor progressMonitor) { + if ((elements != null) && (elements.length > 0)) { + IJavaElement element = elements[0]; + + try { + if (element instanceof IMember) { + IMember member = (IMember) element; + IType type = member.getDeclaringType(); + + if (type.isInterface()) { + IType[] implementingTypes = findImplementingTypes(type, + progressMonitor); + + if (member.getElementType() == IJavaElement.METHOD) { + return findMethods((IMethod)member, implementingTypes, progressMonitor); + } else { + return implementingTypes; + } + } + } + } catch (JavaModelException e) { + JavaPlugin.log(e); + } + } + + return null; + } + + /** + * Searches for interfaces which are implemented by the declaring classes of the + * specified Java elements. Currently, only IMethod instances are searched for. + * Also, only the first element of the elements parameter is taken into + * consideration. + * + * @param elements + * + * @return An array of found interfaces implemented by the declaring classes of the + * specified Java elements (currently only IMethod instances) + */ + public IJavaElement[] searchForInterfaces(IJavaElement[] elements, + IProgressMonitor progressMonitor) { + if ((elements != null) && (elements.length > 0)) { + IJavaElement element = elements[0]; + + if (element instanceof IMember) { + IMember member = (IMember) element; + IType type = member.getDeclaringType(); + + IType[] implementingTypes = findInterfaces(type, progressMonitor); + + if (!progressMonitor.isCanceled()) { + if (member.getElementType() == IJavaElement.METHOD) { + return findMethods((IMethod)member, implementingTypes, progressMonitor); + } else { + return implementingTypes; + } + } + } + } + + return null; + } + + private IImplementorFinder[] getImplementorFinders() { + return IMPLEMENTOR_FINDERS; + } + + private IType[] findImplementingTypes(IType type, IProgressMonitor progressMonitor) { + Collection implementingTypes = new ArrayList<>(); + + IImplementorFinder[] finders = getImplementorFinders(); + + for (int i = 0; (i < finders.length) && !progressMonitor.isCanceled(); i++) { + Collection types = finders[i].findImplementingTypes(type, + new SubProgressMonitor(progressMonitor, 10, + SubProgressMonitor.SUPPRESS_SUBTASK_LABEL)); + + if (types != null) { + implementingTypes.addAll(types); + } + } + + return implementingTypes.toArray(new IType[implementingTypes.size()]); + } + + private IType[] findInterfaces(IType type, IProgressMonitor progressMonitor) { + Collection interfaces = new ArrayList<>(); + + IImplementorFinder[] finders = getImplementorFinders(); + + for (int i = 0; (i < finders.length) && !progressMonitor.isCanceled(); i++) { + Collection types = finders[i].findInterfaces(type, + new SubProgressMonitor(progressMonitor, 10, + SubProgressMonitor.SUPPRESS_SUBTASK_LABEL)); + + if (types != null) { + interfaces.addAll(types); + } + } + + return interfaces.toArray(new IType[interfaces.size()]); + } + + /** + * Finds IMethod instances on the specified IType instances with identical signatures + * as the specified IMethod parameter. + * + * @param method The method to find "equals" of. + * @param types The types in which the search is performed. + * + * @return An array of methods which match the method parameter. + */ + private IJavaElement[] findMethods(IMethod method, IType[] types, + IProgressMonitor progressMonitor) { + Collection foundMethods = new ArrayList<>(); + + SubProgressMonitor subProgressMonitor = new SubProgressMonitor(progressMonitor, + 10, SubProgressMonitor.SUPPRESS_SUBTASK_LABEL); + subProgressMonitor.beginTask("", types.length); //$NON-NLS-1$ + + try { + for (int i = 0; i < types.length; i++) { + IType type = types[i]; + IMethod[] methods = type.findMethods(method); + + if (methods != null) { + for (int j = 0; j < methods.length; j++) { + foundMethods.add(methods[j]); + } + } + + subProgressMonitor.worked(1); + } + } finally { + subProgressMonitor.done(); + } + + return foundMethods.toArray(new IJavaElement[foundMethods.size()]); + } +} diff --git a/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/corext/callhierarchy/JavaImplementorFinder.java b/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/corext/callhierarchy/JavaImplementorFinder.java new file mode 100644 index 0000000000..cba7b06af8 --- /dev/null +++ b/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/corext/callhierarchy/JavaImplementorFinder.java @@ -0,0 +1,60 @@ +/******************************************************************************* + * Copyright (c) 2000, 2011 IBM Corporation and others. + * 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: + * Jesper Kamstrup Linnet (eclipse@kamstrup-linnet.dk) - initial API and implementation + * (report 36180: Callers/Callees view) + *******************************************************************************/ +package org.eclipse.jdt.ls.core.internal.corext.callhierarchy; + +import java.util.Arrays; +import java.util.Collection; +import java.util.HashSet; + +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.jdt.core.IType; +import org.eclipse.jdt.core.ITypeHierarchy; +import org.eclipse.jdt.core.JavaModelException; +import org.eclipse.jdt.internal.ui.JavaPlugin; + +public class JavaImplementorFinder implements IImplementorFinder { + @Override + public Collection findImplementingTypes(IType type, IProgressMonitor progressMonitor) { + ITypeHierarchy typeHierarchy; + + try { + typeHierarchy = type.newTypeHierarchy(progressMonitor); + + IType[] implementingTypes = typeHierarchy.getAllClasses(); + HashSet result = new HashSet<>(Arrays.asList(implementingTypes)); + + return result; + } catch (JavaModelException e) { + JavaPlugin.log(e); + } + + return null; + } + + @Override + public Collection findInterfaces(IType type, IProgressMonitor progressMonitor) { + ITypeHierarchy typeHierarchy; + + try { + typeHierarchy = type.newSupertypeHierarchy(progressMonitor); + + IType[] interfaces = typeHierarchy.getAllSuperInterfaces(type); + HashSet result = new HashSet<>(Arrays.asList(interfaces)); + + return result; + } catch (JavaModelException e) { + JavaPlugin.log(e); + } + + return null; + } +} diff --git a/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/corext/callhierarchy/MethodCall.java b/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/corext/callhierarchy/MethodCall.java new file mode 100644 index 0000000000..fa8981b9aa --- /dev/null +++ b/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/corext/callhierarchy/MethodCall.java @@ -0,0 +1,74 @@ +/******************************************************************************* + * Copyright (c) 2000, 2011 IBM Corporation and others. + * 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: + * Jesper Kamstrup Linnet (eclipse@kamstrup-linnet.dk) - initial API and implementation + * (report 36180: Callers/Callees view) + *******************************************************************************/ +package org.eclipse.jdt.ls.core.internal.corext.callhierarchy; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +import org.eclipse.jdt.core.IMember; + +public class MethodCall { + private IMember fMember; + private List fCallLocations; + + /** + * @param enclosingElement + */ + public MethodCall(IMember enclosingElement) { + this.fMember = enclosingElement; + } + + /** + * + */ + public Collection getCallLocations() { + return fCallLocations; + } + + public CallLocation getFirstCallLocation() { + if ((fCallLocations != null) && !fCallLocations.isEmpty()) { + return fCallLocations.get(0); + } else { + return null; + } + } + + public boolean hasCallLocations() { + return fCallLocations != null && fCallLocations.size() > 0; + } + + /** + * @return Object + */ + public String getKey() { + return getMember().getHandleIdentifier(); + } + + /** + * + */ + public IMember getMember() { + return fMember; + } + + /** + * @param location + */ + public void addCallLocation(CallLocation location) { + if (fCallLocations == null) { + fCallLocations = new ArrayList<>(); + } + + fCallLocations.add(location); + } +} diff --git a/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/corext/callhierarchy/MethodReferencesSearchRequestor.java b/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/corext/callhierarchy/MethodReferencesSearchRequestor.java new file mode 100644 index 0000000000..25f386973d --- /dev/null +++ b/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/corext/callhierarchy/MethodReferencesSearchRequestor.java @@ -0,0 +1,55 @@ +/******************************************************************************* + * Copyright (c) 2000, 2011 IBM Corporation and others. + * 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: + * Jesper Kamstrup Linnet (eclipse@kamstrup-linnet.dk) - initial API and implementation + * (report 36180: Callers/Callees view) + *******************************************************************************/ +package org.eclipse.jdt.ls.core.internal.corext.callhierarchy; + +import java.util.Map; + +import org.eclipse.jdt.core.IJavaElement; +import org.eclipse.jdt.core.IMember; +import org.eclipse.jdt.core.search.SearchMatch; +import org.eclipse.jdt.core.search.SearchRequestor; + +class MethodReferencesSearchRequestor extends SearchRequestor { + private CallSearchResultCollector fSearchResults; + private boolean fRequireExactMatch = true; + + MethodReferencesSearchRequestor() { + fSearchResults = new CallSearchResultCollector(); + } + + public Map getCallers() { + return fSearchResults.getCallers(); + } + + @Override + public void acceptSearchMatch(SearchMatch match) { + if (fRequireExactMatch && (match.getAccuracy() != SearchMatch.A_ACCURATE)) { + return; + } + + if (match.isInsideDocComment()) { + return; + } + + if (match.getElement() != null && match.getElement() instanceof IMember) { + IMember member= (IMember) match.getElement(); + switch (member.getElementType()) { + case IJavaElement.METHOD: + case IJavaElement.TYPE: + case IJavaElement.FIELD: + case IJavaElement.INITIALIZER: + fSearchResults.addMember(member, member, match.getOffset(), match.getOffset()+match.getLength()); + break; + } + } + } +} diff --git a/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/corext/callhierarchy/MethodWrapper.java b/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/corext/callhierarchy/MethodWrapper.java new file mode 100644 index 0000000000..d0f6d56aaf --- /dev/null +++ b/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/corext/callhierarchy/MethodWrapper.java @@ -0,0 +1,372 @@ +/******************************************************************************* + * Copyright (c) 2000, 2015 IBM Corporation and others. + * 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: + * Jesper Kamstrup Linnet (eclipse@kamstrup-linnet.dk) - initial API and implementation + * (report 36180: Callers/Callees view) + * Stephan Herrmann (stephan@cs.tu-berlin.de): + * - bug 206949: [call hierarchy] filter field accesses (only write or only read) + *******************************************************************************/ +package org.eclipse.jdt.ls.core.internal.corext.callhierarchy; + +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; + +import org.eclipse.core.runtime.Assert; +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.core.runtime.OperationCanceledException; +import org.eclipse.core.runtime.PlatformObject; +import org.eclipse.jdt.core.IJavaElement; +import org.eclipse.jdt.core.IMember; +import org.eclipse.jdt.core.search.IJavaSearchConstants; +import org.eclipse.jdt.internal.ui.callhierarchy.MethodWrapperWorkbenchAdapter; +import org.eclipse.ui.model.IWorkbenchAdapter; + +/** + * This class represents the general parts of a method call (either to or from a + * method). + * + */ +public abstract class MethodWrapper extends PlatformObject { + private Map fElements = null; + + /* + * A cache of previously found methods. This cache should be searched + * before adding a "new" method object reference to the list of elements. + * This way previously found methods won't be searched again. + */ + private Map> fMethodCache; + private final MethodCall fMethodCall; + private final MethodWrapper fParent; + private int fLevel; + /** + * One of {@link IJavaSearchConstants#REFERENCES}, {@link IJavaSearchConstants#READ_ACCESSES}, + * or {@link IJavaSearchConstants#WRITE_ACCESSES}, or 0 if not set. Only used for root wrappers. + */ + private int fFieldSearchMode; + + public MethodWrapper(MethodWrapper parent, MethodCall methodCall) { + Assert.isNotNull(methodCall); + + if (parent == null) { + setMethodCache(new HashMap>()); + fLevel = 1; + } else { + setMethodCache(parent.getMethodCache()); + fLevel = parent.getLevel() + 1; + } + + this.fMethodCall = methodCall; + this.fParent = parent; + } + + @SuppressWarnings("unchecked") + @Override + public T getAdapter(Class adapter) { + if (adapter == IJavaElement.class) { + return (T) getMember(); + } else if (adapter == IWorkbenchAdapter.class){ + return (T) new MethodWrapperWorkbenchAdapter(this); + } else { + return null; + } + } + + public MethodWrapper[] getCalls(IProgressMonitor progressMonitor) { + if (fElements == null) { + doFindChildren(progressMonitor); + } + + MethodWrapper[] result = new MethodWrapper[fElements.size()]; + int i = 0; + + for (Iterator iter = fElements.keySet().iterator(); iter.hasNext();) { + MethodCall methodCall = getMethodCallFromMap(fElements, iter.next()); + result[i++] = createMethodWrapper(methodCall); + } + + return result; + } + + public int getLevel() { + return fLevel; + } + + public IMember getMember() { + return getMethodCall().getMember(); + } + + public MethodCall getMethodCall() { + return fMethodCall; + } + + public String getName() { + if (getMethodCall() != null) { + return getMethodCall().getMember().getElementName(); + } else { + return ""; //$NON-NLS-1$ + } + } + + public MethodWrapper getParent() { + return fParent; + } + + public int getFieldSearchMode() { + if (fFieldSearchMode != 0) { + return fFieldSearchMode; + } + MethodWrapper parent= getParent(); + while (parent != null) { + if (parent.fFieldSearchMode != 0) { + return parent.fFieldSearchMode; + } else { + parent= parent.getParent(); + } + } + return IJavaSearchConstants.REFERENCES; + } + + public void setFieldSearchMode(int fieldSearchMode) { + fFieldSearchMode= fieldSearchMode; + } + + @Override + public boolean equals(Object oth) { + if (this == oth) { + return true; + } + + if (oth == null) { + return false; + } + + if (oth instanceof MethodWrapperWorkbenchAdapter) { + //Note: A MethodWrapper is equal to a referring MethodWrapperWorkbenchAdapter and vice versa (bug 101677). + oth= ((MethodWrapperWorkbenchAdapter) oth).getMethodWrapper(); + } + + if (oth.getClass() != getClass()) { + return false; + } + + MethodWrapper other = (MethodWrapper) oth; + + if (this.fParent == null) { + if (other.fParent != null) { + return false; + } + } else { + if (!this.fParent.equals(other.fParent)) { + return false; + } + } + + if (this.getMethodCall() == null) { + if (other.getMethodCall() != null) { + return false; + } + } else { + if (!this.getMethodCall().equals(other.getMethodCall())) { + return false; + } + } + + return true; + } + + @Override + public int hashCode() { + final int PRIME = 1000003; + int result = 0; + + if (fParent != null) { + result = (PRIME * result) + fParent.hashCode(); + } + + if (getMethodCall() != null) { + result = (PRIME * result) + getMethodCall().getMember().hashCode(); + } + + return result; + } + + private void setMethodCache(Map> methodCache) { + fMethodCache = methodCache; + } + + protected abstract String getTaskName(); + + private void addCallToCache(MethodCall methodCall) { + Map cachedCalls = lookupMethod(this.getMethodCall()); + cachedCalls.put(methodCall.getKey(), methodCall); + } + + /** + * Creates a method wrapper for the child of the receiver. + * + * @param methodCall the method call + * @return the method wrapper + */ + protected abstract MethodWrapper createMethodWrapper(MethodCall methodCall); + + private void doFindChildren(IProgressMonitor progressMonitor) { + Map existingResults = lookupMethod(getMethodCall()); + + if (existingResults != null && !existingResults.isEmpty()) { + fElements = new HashMap<>(); + fElements.putAll(existingResults); + } else { + initCalls(); + + if (progressMonitor != null) { + progressMonitor.beginTask(getTaskName(), 100); + } + + try { + performSearch(progressMonitor); + } catch (OperationCanceledException e){ + fElements= null; + throw e; + } finally { + if (progressMonitor != null) { + progressMonitor.done(); + } + } + } + } + + /** + * Determines if the method represents a recursion call (i.e. whether the + * method call is already in the cache.) + * + * @return True if the call is part of a recursion + */ + public boolean isRecursive() { + if (fParent instanceof RealCallers) { + return false; + } + MethodWrapper current = getParent(); + + while (current != null) { + if (getMember().getHandleIdentifier().equals(current.getMember() + .getHandleIdentifier())) { + return true; + } + + current = current.getParent(); + } + + return false; + } + + /** + * @return whether this member can have children + */ + public abstract boolean canHaveChildren(); + + /** + * This method finds the children of the current IMember (either callers or + * callees, depending on the concrete subclass). + * @param progressMonitor a progress monitor + * + * @return a map from handle identifier ({@link String}) to {@link MethodCall} + */ + protected abstract Map findChildren(IProgressMonitor progressMonitor); + + private Map> getMethodCache() { + return fMethodCache; + } + + private void initCalls() { + this.fElements = new HashMap<>(); + + initCacheForMethod(); + } + + /** + * Looks up a previously created search result in the "global" cache. + * @param methodCall the method call + * @return the List of previously found search results + */ + private Map lookupMethod(MethodCall methodCall) { + return getMethodCache().get(methodCall.getKey()); + } + + private void performSearch(IProgressMonitor progressMonitor) { + fElements = findChildren(progressMonitor); + + for (Iterator iter = fElements.keySet().iterator(); iter.hasNext();) { + checkCanceled(progressMonitor); + + MethodCall methodCall = getMethodCallFromMap(fElements, iter.next()); + addCallToCache(methodCall); + } + } + + private MethodCall getMethodCallFromMap(Map elements, String key) { + return elements.get(key); + } + + private void initCacheForMethod() { + Map cachedCalls = new HashMap<>(); + getMethodCache().put(this.getMethodCall().getKey(), cachedCalls); + } + + /** + * Checks with the progress monitor to see whether the creation of the type hierarchy + * should be canceled. Should be regularly called + * so that the user can cancel. + * + * @param progressMonitor the progress monitor + * @exception OperationCanceledException if cancelling the operation has been requested + * @see IProgressMonitor#isCanceled + */ + protected void checkCanceled(IProgressMonitor progressMonitor) { + if (progressMonitor != null && progressMonitor.isCanceled()) { + throw new OperationCanceledException(); + } + } + + /** + * Allows a visitor to traverse the call hierarchy. The visiting is stopped when + * a recursive node is reached. + * + * @param visitor the visitor + * @param progressMonitor the progress monitor + */ + public void accept(CallHierarchyVisitor visitor, IProgressMonitor progressMonitor) { + if (getParent() != null && getParent().isRecursive()) { + return; + } + checkCanceled(progressMonitor); + + visitor.preVisit(this); + if (visitor.visit(this)) { + MethodWrapper[] methodWrappers= getCalls(progressMonitor); + for (int i= 0; i < methodWrappers.length; i++) { + methodWrappers[i].accept(visitor, progressMonitor); + } + } + visitor.postVisit(this); + + if (progressMonitor != null) { + progressMonitor.worked(1); + } + } + + /** + * Removes the given method call from the cache. + * + * @since 3.6 + */ + public void removeFromCache() { + fElements= null; + fMethodCache.remove(getMethodCall().getKey()); + } +} diff --git a/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/corext/callhierarchy/RealCallers.java b/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/corext/callhierarchy/RealCallers.java new file mode 100644 index 0000000000..16e6a72fac --- /dev/null +++ b/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/corext/callhierarchy/RealCallers.java @@ -0,0 +1,39 @@ +/******************************************************************************* + * Copyright (c) 2009, 2011 IBM Corporation and others. + * 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: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +package org.eclipse.jdt.ls.core.internal.corext.callhierarchy; + +/** + * Class for the real callers. + * + * @since 3.5 + */ + public class RealCallers extends CallerMethodWrapper { + + /** + * Sets the parent method wrapper. + * + * @param methodWrapper the method wrapper + * @param methodCall the method call + */ + public RealCallers(MethodWrapper methodWrapper, MethodCall methodCall) { + super(methodWrapper, methodCall); + } + + @Override + public boolean canHaveChildren() { + return true; + } + + @Override + public boolean isRecursive() { + return false; + } + }