blob: 35bacf4c71778d03287c275bdd10e1a811e71744 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2000, 2012 IBM Corporation and others.
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* 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.internal.ui.callhierarchy;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.core.runtime.Assert;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.jface.dialogs.ErrorDialog;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.jface.preference.IPreferenceStore;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.viewers.StructuredSelection;
import org.eclipse.ui.IEditorPart;
import org.eclipse.ui.IViewReference;
import org.eclipse.ui.IWorkbenchPage;
import org.eclipse.ui.IWorkbenchWindow;
import org.eclipse.ui.PartInitException;
import org.eclipse.ui.texteditor.ITextEditor;
import org.eclipse.jdt.core.IJavaElement;
import org.eclipse.jdt.core.IMember;
import org.eclipse.jdt.core.ISourceRange;
import org.eclipse.jdt.core.JavaModelException;
import org.eclipse.jdt.internal.corext.callhierarchy.CallHierarchy;
import org.eclipse.jdt.internal.corext.callhierarchy.CallLocation;
import org.eclipse.jdt.internal.corext.callhierarchy.MethodWrapper;
import org.eclipse.jdt.internal.corext.util.Messages;
import org.eclipse.jdt.ui.JavaUI;
import org.eclipse.jdt.internal.ui.IJavaStatusConstants;
import org.eclipse.jdt.internal.ui.JavaPlugin;
import org.eclipse.jdt.internal.ui.actions.SelectionConverter;
import org.eclipse.jdt.internal.ui.javaeditor.EditorUtility;
import org.eclipse.jdt.internal.ui.util.ExceptionHandler;
public class CallHierarchyUI {
private static final int DEFAULT_MAX_CALL_DEPTH= 10;
private static final String PREF_MAX_CALL_DEPTH = "PREF_MAX_CALL_DEPTH"; //$NON-NLS-1$
private static CallHierarchyUI fgInstance;
private int fViewCount= 0;
private final List<IMember[]> fMethodHistory= new ArrayList<>();
/**
* List of the Call Hierarchy views in LRU order, where the most recently used view is at index 0.
* @since 3.7
*/
private List<CallHierarchyViewPart> fLRUCallHierarchyViews= new ArrayList<>();
private CallHierarchyUI() {
// Do nothing
}
public static CallHierarchyUI getDefault() {
if (fgInstance == null) {
fgInstance = new CallHierarchyUI();
}
return fgInstance;
}
/**
* Returns the maximum tree level allowed
* @return int
*/
public int getMaxCallDepth() {
int maxCallDepth;
IPreferenceStore settings = JavaPlugin.getDefault().getPreferenceStore();
maxCallDepth = settings.getInt(PREF_MAX_CALL_DEPTH);
if (maxCallDepth < 1 || maxCallDepth > 99) {
maxCallDepth= DEFAULT_MAX_CALL_DEPTH;
}
return maxCallDepth;
}
public void setMaxCallDepth(int maxCallDepth) {
IPreferenceStore settings = JavaPlugin.getDefault().getPreferenceStore();
settings.setValue(PREF_MAX_CALL_DEPTH, maxCallDepth);
}
public static void jumpToMember(IJavaElement element) {
if (element != null) {
try {
JavaUI.openInEditor(element, true, true);
} catch (JavaModelException e) {
JavaPlugin.log(e);
} catch (PartInitException e) {
JavaPlugin.log(e);
}
}
}
public static void jumpToLocation(CallLocation callLocation) {
try {
IEditorPart methodEditor = JavaUI.openInEditor(callLocation.getMember(), false, false);
if (methodEditor instanceof ITextEditor) {
ITextEditor editor = (ITextEditor) methodEditor;
editor.selectAndReveal(callLocation.getStart(),
(callLocation.getEnd() - callLocation.getStart()));
}
} catch (JavaModelException e) {
JavaPlugin.log(e);
} catch (PartInitException e) {
JavaPlugin.log(e);
}
}
/**
* Opens the element in the editor or shows an error dialog if that fails.
*
* @param element the element to open
* @param shell parent shell for error dialog
* @param activateOnOpen <code>true</code> if the editor should be activated
* @return <code>true</code> iff no error occurred while trying to open the editor,
* <code>false</code> iff an error dialog was raised.
*/
public static boolean openInEditor(Object element, Shell shell, boolean activateOnOpen) {
CallLocation callLocation= CallHierarchy.getCallLocation(element);
try {
IMember enclosingMember;
int selectionStart;
int selectionLength;
if (callLocation != null) {
enclosingMember= callLocation.getMember();
selectionStart= callLocation.getStart();
selectionLength= callLocation.getEnd() - selectionStart;
} else if (element instanceof MethodWrapper) {
enclosingMember= ((MethodWrapper) element).getMember();
ISourceRange selectionRange= enclosingMember.getNameRange();
if (selectionRange == null)
selectionRange= enclosingMember.getSourceRange();
if (selectionRange == null)
return true;
selectionStart= selectionRange.getOffset();
selectionLength= selectionRange.getLength();
} else {
return true;
}
IEditorPart methodEditor = JavaUI.openInEditor(enclosingMember, activateOnOpen, false);
if (methodEditor instanceof ITextEditor) {
ITextEditor editor = (ITextEditor) methodEditor;
editor.selectAndReveal(selectionStart, selectionLength);
}
return true;
} catch (JavaModelException e) {
JavaPlugin.log(new Status(IStatus.ERROR, JavaPlugin.getPluginId(),
IJavaStatusConstants.INTERNAL_ERROR,
CallHierarchyMessages.CallHierarchyUI_open_in_editor_error_message, e));
ErrorDialog.openError(shell, CallHierarchyMessages.OpenLocationAction_error_title,
CallHierarchyMessages.CallHierarchyUI_open_in_editor_error_message,
e.getStatus());
return false;
} catch (PartInitException x) {
String name;
if (callLocation != null)
name= callLocation.getCalledMember().getElementName();
else if (element instanceof MethodWrapper)
name= ((MethodWrapper) element).getName();
else
name= ""; //$NON-NLS-1$
MessageDialog.openError(shell, CallHierarchyMessages.OpenLocationAction_error_title,
Messages.format(
CallHierarchyMessages.CallHierarchyUI_open_in_editor_error_messageArgs,
new String[] { name, x.getMessage() }));
return false;
}
}
public static IEditorPart isOpenInEditor(Object elem) {
IJavaElement javaElement= null;
if (elem instanceof MethodWrapper) {
javaElement= ((MethodWrapper) elem).getMember();
} else if (elem instanceof CallLocation) {
javaElement= ((CallLocation) elem).getMember();
}
if (javaElement != null) {
return EditorUtility.isOpenInEditor(javaElement);
}
return null;
}
public static CallHierarchyViewPart openSelectionDialog(IMember[] candidates, IWorkbenchWindow window) {
Assert.isTrue(candidates != null);
IMember input= null;
if (candidates.length > 1) {
String title= CallHierarchyMessages.CallHierarchyUI_selectionDialog_title;
String message= CallHierarchyMessages.CallHierarchyUI_selectionDialog_message;
input= (IMember) SelectionConverter.selectJavaElement(candidates, window.getShell(), title, message);
} else if (candidates.length == 1) {
input= candidates[0];
}
if (input == null)
return openView(new IMember[] {}, window);
return openView(new IMember[] { input }, window);
}
public static CallHierarchyViewPart openView(IMember[] input, IWorkbenchWindow window) {
if (input.length == 0) {
MessageDialog.openInformation(window.getShell(), CallHierarchyMessages.CallHierarchyUI_selectionDialog_title,
CallHierarchyMessages.CallHierarchyUI_open_operation_unavialable);
return null;
}
IWorkbenchPage page= window.getActivePage();
try {
CallHierarchyViewPart viewPart= getDefault().findLRUCallHierarchyViewPart(page); //find the first view which is not pinned
String secondaryId= null;
if (viewPart == null) {
if (page.findViewReference(CallHierarchyViewPart.ID_CALL_HIERARCHY) != null) //all the current views are pinned, open a new instance
secondaryId= String.valueOf(++getDefault().fViewCount);
} else
secondaryId= viewPart.getViewSite().getSecondaryId();
viewPart= (CallHierarchyViewPart)page.showView(CallHierarchyViewPart.ID_CALL_HIERARCHY, secondaryId, IWorkbenchPage.VIEW_ACTIVATE);
viewPart.setInputElements(input);
return viewPart;
} catch (CoreException e) {
ExceptionHandler.handle(e, window.getShell(),
CallHierarchyMessages.CallHierarchyUI_error_open_view, e.getMessage());
}
return null;
}
/**
* Finds the first Call Hierarchy view part instance that is not pinned.
*
* @param page the active page
* @return the Call Hierarchy view part to open or <code>null</code> if none found
* @since 3.7
*/
private CallHierarchyViewPart findLRUCallHierarchyViewPart(IWorkbenchPage page) {
boolean viewFoundInPage= false;
for (CallHierarchyViewPart view : fLRUCallHierarchyViews) {
if (page.equals(view.getSite().getPage())) {
if (!view.isPinned()) {
return view;
}
viewFoundInPage= true;
}
}
if (!viewFoundInPage) {
// find unresolved views
for (IViewReference curr : page.getViewReferences()) {
if (CallHierarchyViewPart.ID_CALL_HIERARCHY.equals(curr.getId()) && page.equals(curr.getPage())) {
CallHierarchyViewPart view= (CallHierarchyViewPart)curr.getView(true);
if (view != null && !view.isPinned()) {
return view;
}
}
}
}
return null;
}
/**
* Adds the activated view part to the head of the list.
*
* @param view the Call Hierarchy view part
* @since 3.7
*/
void callHierarchyViewActivated(CallHierarchyViewPart view) {
fLRUCallHierarchyViews.remove(view);
fLRUCallHierarchyViews.add(0, view);
}
/**
* Removes the closed view part from the list.
*
* @param view the closed view part
* @since 3.7
*/
void callHierarchyViewClosed(CallHierarchyViewPart view) {
fLRUCallHierarchyViews.remove(view);
}
/**
* Converts an ISelection (containing MethodWrapper instances) to an ISelection
* with the MethodWrapper's replaced by their corresponding IMembers. If the selection
* contains elements which are not MethodWrapper instances or not already IMember instances
* they are discarded.
* @param selection The selection to convert.
* @return An ISelection containing IMember's in place of MethodWrapper instances.
*/
static ISelection convertSelection(ISelection selection) {
if (selection.isEmpty()) {
return selection;
}
if (selection instanceof IStructuredSelection) {
IStructuredSelection structuredSelection= (IStructuredSelection) selection;
List<IMember> javaElements= new ArrayList<>();
for (Iterator<?> iter= structuredSelection.iterator(); iter.hasNext();) {
Object element= iter.next();
if (element instanceof MethodWrapper) {
IMember member= ((MethodWrapper)element).getMember();
if (member != null) {
javaElements.add(member);
}
} else if (element instanceof IMember) {
javaElements.add((IMember) element);
} else if (element instanceof CallLocation) {
IMember member = ((CallLocation) element).getMember();
javaElements.add(member);
}
}
return new StructuredSelection(javaElements);
}
return StructuredSelection.EMPTY;
}
/**
* Clears the history and updates all the open views.
*
* @since 3.7
*/
void clearHistory() {
for (CallHierarchyViewPart part : fLRUCallHierarchyViews) {
part.setHistoryEntries(new IMember[0][]);
part.setInputElements(null);
}
}
/**
* Returns the method history.
*
* @return the method history
* @since 3.7
*/
List<IMember[]> getMethodHistory() {
return fMethodHistory;
}
}