org.eclipse.jdt.bcoview.views.BytecodeOutlineView Maven / Gradle / Ivy
/*******************************************************************************
* Copyright (c) 2023 Andrey Loskutov 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:
* Andrey Loskutov - initial API and implementation
*******************************************************************************/
package org.eclipse.jdt.bcoview.views;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.BitSet;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.ResourceBundle;
import java.util.Set;
import org.objectweb.asm.tree.ClassNode;
import org.objectweb.asm.util.Printer;
import org.eclipse.jdt.bcoview.BytecodeOutlinePlugin;
import org.eclipse.jdt.bcoview.asm.DecompiledClass;
import org.eclipse.jdt.bcoview.asm.DecompiledMethod;
import org.eclipse.jdt.bcoview.asm.DecompilerHelper;
import org.eclipse.jdt.bcoview.asm.DecompilerOptions;
import org.eclipse.jdt.bcoview.asm.LineRange;
import org.eclipse.jdt.bcoview.internal.Messages;
import org.eclipse.jdt.bcoview.preferences.BCOConstants;
import org.eclipse.jdt.bcoview.ui.EclipseUtils;
import org.eclipse.jdt.bcoview.ui.JdtUtils;
import org.eclipse.jdt.bcoview.ui.actions.DefaultToggleAction;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.SashForm;
import org.eclipse.swt.custom.StackLayout;
import org.eclipse.swt.custom.StyledText;
import org.eclipse.swt.events.ControlEvent;
import org.eclipse.swt.events.ControlListener;
import org.eclipse.swt.events.KeyEvent;
import org.eclipse.swt.events.KeyListener;
import org.eclipse.swt.events.MouseAdapter;
import org.eclipse.swt.events.MouseEvent;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Menu;
import org.eclipse.swt.widgets.Table;
import org.eclipse.swt.widgets.TableColumn;
import org.eclipse.swt.widgets.TableItem;
import org.eclipse.swt.widgets.Widget;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.filebuffers.FileBuffers;
import org.eclipse.jface.action.Action;
import org.eclipse.jface.action.IAction;
import org.eclipse.jface.action.IMenuManager;
import org.eclipse.jface.action.IToolBarManager;
import org.eclipse.jface.action.MenuManager;
import org.eclipse.jface.action.Separator;
import org.eclipse.jface.action.StatusLineManager;
import org.eclipse.jface.preference.IPreferenceStore;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.ISelectionChangedListener;
import org.eclipse.jface.viewers.TableViewer;
import org.eclipse.jface.text.BadLocationException;
import org.eclipse.jface.text.Document;
import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.text.IFindReplaceTarget;
import org.eclipse.jface.text.IRegion;
import org.eclipse.jface.text.ITextHover;
import org.eclipse.jface.text.ITextListener;
import org.eclipse.jface.text.ITextOperationTarget;
import org.eclipse.jface.text.ITextSelection;
import org.eclipse.jface.text.ITextViewer;
import org.eclipse.jface.text.TextViewer;
import org.eclipse.jface.text.hyperlink.IHyperlink;
import org.eclipse.jface.text.hyperlink.IHyperlinkDetector;
import org.eclipse.jface.text.quickassist.IQuickAssistAssistant;
import org.eclipse.jface.text.source.ISourceViewer;
import org.eclipse.ui.IActionBars;
import org.eclipse.ui.IEditorPart;
import org.eclipse.ui.IEditorReference;
import org.eclipse.ui.ISelectionService;
import org.eclipse.ui.ISharedImages;
import org.eclipse.ui.IViewSite;
import org.eclipse.ui.IWorkbenchActionConstants;
import org.eclipse.ui.IWorkbenchCommandConstants;
import org.eclipse.ui.IWorkbenchPart;
import org.eclipse.ui.IWorkbenchWindow;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.actions.ActionFactory;
import org.eclipse.ui.console.actions.TextViewerAction;
import org.eclipse.ui.part.ViewPart;
import org.eclipse.ui.plugin.AbstractUIPlugin;
import org.eclipse.ui.texteditor.FindReplaceAction;
import org.eclipse.ui.texteditor.ITextEditor;
import org.eclipse.ui.texteditor.IUpdate;
import org.eclipse.jdt.core.IJavaElement;
import org.eclipse.jdt.core.IType;
import org.eclipse.jdt.core.JavaModelException;
import org.eclipse.jdt.core.search.IJavaSearchScope;
import org.eclipse.jdt.core.search.SearchEngine;
import org.eclipse.jdt.ui.actions.OpenAction;
import org.eclipse.jdt.ui.actions.SelectionDispatchAction;
import org.eclipse.jdt.ui.text.IColorManager;
import org.eclipse.jdt.ui.text.IJavaPartitions;
import org.eclipse.jdt.ui.text.JavaSourceViewerConfiguration;
import org.eclipse.jdt.ui.text.JavaTextTools;
import org.eclipse.jdt.internal.ui.JavaPlugin;
import org.eclipse.jdt.internal.ui.javaeditor.JavaElementHyperlink;
import org.eclipse.jdt.internal.ui.javaeditor.JavaElementHyperlinkDetector;
import org.eclipse.jdt.internal.ui.javaeditor.JavaSourceViewer;
import org.eclipse.jdt.internal.ui.text.JavaWordFinder;
import org.eclipse.jdt.internal.ui.text.java.hover.JavadocBrowserInformationControlInput;
import org.eclipse.jdt.internal.ui.text.java.hover.JavadocHover;
/**
* This view shows decompiled java bytecode
*/
@SuppressWarnings("restriction")
public class BytecodeOutlineView extends ViewPart implements IBytecodePart {
// orientations
static final int VIEW_ORIENTATION_VERTICAL = 0;
static final int VIEW_ORIENTATION_HORIZONTAL = 1;
static final int VIEW_ORIENTATION_AUTOMATIC = 2;
/**
* The current orientation; either VIEW_ORIENTATION_HORIZONTAL
* VIEW_ORIENTATION_VERTICAL
, or VIEW_ORIENTATION_AUTOMATIC
.
*/
int orientation = VIEW_ORIENTATION_AUTOMATIC;
/**
* The current orientation; either VIEW_ORIENTATION_HORIZONTAL
* VIEW_ORIENTATION_VERTICAL
.
*/
private int currentOrientation;
protected ToggleOrientationAction[] toggleOrientationActions;
protected BitSet modes;
protected boolean inputChanged;
protected boolean bufferIsDirty;
private boolean isEnabled;
private boolean isActive;
private boolean isVisible;
protected Composite stackComposite;
protected StyledText textControl;
protected JavaSourceViewer textViewer;
protected SashForm verifyControl;
protected SashForm stackAndLvt;
protected Table tableControl;
protected TableViewer tableControlViewer;
protected Table stackTable;
protected Table lvtTable;
protected ITextEditor javaEditor;
private IJavaElement javaInput;
protected IJavaElement lastChildElement;
protected ITextSelection currentSelection;
protected EditorListener editorListener;
protected Action selectionChangedAction;
protected Action refreshVarsAndStackAction;
protected DefaultToggleAction linkWithEditorAction;
protected DefaultToggleAction showSelectedOnlyAction;
protected DefaultToggleAction setRawModeAction;
protected DefaultToggleAction toggleASMifierModeAction;
protected DefaultToggleAction hideLineInfoAction;
protected DefaultToggleAction hideLocalsAction;
protected DefaultToggleAction hideStackMapAction;
protected DefaultToggleAction showHexValuesAction;
protected DefaultToggleAction expandStackMapAction;
protected DefaultToggleAction toggleVerifierAction;
protected StatusLineManager statusLineManager;
protected BCOViewSelectionProvider viewSelectionProvider;
protected Color errorColor;
private DecompiledClass lastDecompiledResult;
protected Map globalActions;
protected List selectionActions;
private MenuManager contextMenuManager;
/** global class info, without current selection status */
protected String currentStatusMessage;
protected boolean hasAnalyzerError;
/*
* I don't know how to update the state of toolbar and menu managers because it seems
* that if we toggle the action state internally (not by user click) then either the
* managers or contribution items or whatever holds the old state of checked action.
* This flag is a workaround and allows us restore the state after internal toggling.
*/
private boolean restoreVerify;
// updates the find replace action if the document length is > 0
private ITextListener textListener;
// see org.eclipse.ui.console.TextConsolePage for the reason to do this ;)
private ISelectionChangedListener textSelectionListener;
private Control statusControl;
protected void setJavaInput(IJavaElement javaInput) {
this.javaInput = javaInput;
inputChanged = true;
}
public BytecodeOutlineView() {
super();
modes = new BitSet();
globalActions = new HashMap<>();
selectionActions = new ArrayList<>();
}
/**
* Is this view state changes depending on editor changes?
*
* @return true if linked with editor
*/
protected boolean isLinkedWithEditor() {
return modes.get(BCOConstants.F_LINK_VIEW_TO_EDITOR);
}
/**
* Are actions on toolbar active?
*
* @return Returns the isEnabled.
*/
private boolean isEnabled() {
return isEnabled;
}
private void setEnabled(boolean on) {
this.isEnabled = on;
if (tableControl != null && !tableControl.isDisposed()) {
tableControl.setEnabled(on);
}
if (stackTable != null && !stackTable.isDisposed()) {
stackTable.setEnabled(on);
}
if (lvtTable != null && !lvtTable.isDisposed()) {
lvtTable.setEnabled(on);
}
showSelectedOnlyAction.setEnabled(on);
// linkWithEditorAction.setEnabled(on);
selectionChangedAction.setEnabled(on);
toggleVerifierAction.setEnabled(on);
hideLocalsAction.setEnabled(on);
hideLineInfoAction.setEnabled(on);
hideStackMapAction.setEnabled(on);
showHexValuesAction.setEnabled(on);
toggleASMifierModeAction.setEnabled(on);
expandStackMapAction.setEnabled(on);
setRawModeAction.setEnabled(on && !toggleASMifierModeAction.isChecked());
boolean showAnalyzer = on && toggleVerifierAction.isChecked();
for (ToggleOrientationAction toggleOrientationAction : toggleOrientationActions) {
toggleOrientationAction.setEnabled(showAnalyzer);
}
}
/**
* Is this view monitoring workspace changes?
*
* @return Returns the isActive.
*/
private boolean isActive() {
return isActive;
}
private void setBufferIsDirty(boolean bufferIsDirty) {
this.bufferIsDirty = bufferIsDirty;
}
private void setInput(ITextEditor editor) {
javaEditor = null;
setJavaInput(null);
lastDecompiledResult = null;
if (editor != null) {
IJavaElement javaElem = EclipseUtils.getJavaInput(editor);
if (javaElem == null) {
return;
}
setJavaInput(javaElem);
javaEditor = editor;
checkVerifyMode();
updateSelection(EclipseUtils.getSelection(javaEditor.getSelectionProvider()));
setBufferIsDirty(editor.isDirty());
}
}
private void checkVerifyMode() {
if (toggleVerifierAction == null) {
return;
}
boolean aoi = JdtUtils.isAbstractOrInterface(javaInput);
if (!toggleVerifierAction.isChecked()) {
// deactivate verify button, but only if *not* in verify mode
toggleVerifierAction.setEnabled(!aoi);
restoreVerify = false;
} else {
if (aoi) {
// swith verify mode off, because it is not applicable to selected element
inputChanged = true;
toggleVerifyMode(getViewSite().getActionBars().getMenuManager(), false);
// remember last state, to match the state of the toolbars and menus
restoreVerify = true;
} else {
if (restoreVerify) {
inputChanged = true;
toggleVerifierAction.setEnabled(true);
toggleVerifyMode(getViewSite().getActionBars().getMenuManager(), true);
}
restoreVerify = false;
}
}
}
private boolean updateSelection(ITextSelection sel) {
if (sel != null
&& (sel.equals(currentSelection) || (currentSelection != null
&& sel.getStartLine() == currentSelection.getStartLine() && sel
.getEndLine() == currentSelection.getEndLine()))) {
/*
* getStartLine/getEndLine is probably not sensitive enough - but in case of
* java classes/methods which fits in one selection but not in the other, then
* I think we can ignore them here - this is not the 99% of use cases.
*/
return false;
}
currentSelection = sel;
return true;
}
@Override
public void init(IViewSite site) {
super.setSite(site);
if (editorListener == null) {
editorListener = new EditorListener(this);
getSite().getWorkbenchWindow().getPartService().addPartListener(editorListener);
}
}
@Override
public void createPartControl(Composite parent) {
errorColor = parent.getDisplay().getSystemColor(SWT.COLOR_RED);
parent.addControlListener(new ControlListener() {
@Override
public void controlMoved(ControlEvent e) {
//
}
@Override
public void controlResized(ControlEvent e) {
computeOrientation();
}
});
GridLayout parentLayout = new GridLayout();
parentLayout.numColumns = 1;
parentLayout.marginBottom = -5;
parentLayout.marginTop = -5;
parentLayout.marginLeft = -5;
parentLayout.marginRight = -5;
parent.setLayout(parentLayout);
stackComposite = new Composite(parent, SWT.NONE);
stackComposite.setLayoutData(new GridData(GridData.FILL_BOTH));
stackComposite.setLayout(new StackLayout());
statusLineManager = new StatusLineManager();
statusControl = statusLineManager.createControl(parent, SWT.NONE);
statusControl.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
IEditorPart activeEditor = EclipseUtils.getActiveEditor();
if (activeEditor instanceof ITextEditor) {
setInput((ITextEditor) activeEditor);
}
createTextControl();
createTextContextMenu();
createVerifyControl();
initModes();
if (modes.get(BCOConstants.F_SHOW_ANALYZER)) {
((StackLayout) stackComposite.getLayout()).topControl = verifyControl;
} else {
((StackLayout) stackComposite.getLayout()).topControl = textControl;
}
createSelectionProvider();
createToolbarActions();
setEnabled(false);
}
private void initModes() {
IPreferenceStore store = BytecodeOutlinePlugin.getDefault().getPreferenceStore();
modes.set(BCOConstants.F_LINK_VIEW_TO_EDITOR, store.getBoolean(BCOConstants.LINK_VIEW_TO_EDITOR));
modes.set(BCOConstants.F_SHOW_ONLY_SELECTED_ELEMENT, store.getBoolean(BCOConstants.SHOW_ONLY_SELECTED_ELEMENT));
modes.set(BCOConstants.F_SHOW_RAW_BYTECODE, store.getBoolean(BCOConstants.SHOW_RAW_BYTECODE));
modes.set(BCOConstants.F_SHOW_LINE_INFO, store.getBoolean(BCOConstants.SHOW_LINE_INFO));
modes.set(BCOConstants.F_SHOW_VARIABLES, store.getBoolean(BCOConstants.SHOW_VARIABLES));
modes.set(BCOConstants.F_SHOW_STACKMAP, store.getBoolean(BCOConstants.SHOW_STACKMAP));
modes.set(BCOConstants.F_EXPAND_STACKMAP, store.getBoolean(BCOConstants.EXPAND_STACKMAP));
modes.set(BCOConstants.F_SHOW_ASMIFIER_CODE, store.getBoolean(BCOConstants.SHOW_ASMIFIER_CODE));
modes.set(BCOConstants.F_SHOW_ANALYZER, store.getBoolean(BCOConstants.SHOW_ANALYZER));
modes.set(BCOConstants.F_SHOW_HEX_VALUES, store.getBoolean(BCOConstants.SHOW_HEX_VALUES));
}
private void createToolbarActions() {
createTextActions();
final IActionBars bars = getViewSite().getActionBars();
final IToolBarManager tmanager = bars.getToolBarManager();
final IMenuManager mmanager = bars.getMenuManager();
selectionChangedAction = new Action() {
@Override
public void run() {
Point selection = textControl.getSelection();
setSelectionInJavaEditor(selection);
}
};
refreshVarsAndStackAction = new Action() {
@Override
public void run() {
int selectionIndex = tableControl.getSelectionIndex();
TableItem[] items = tableControl.getSelection();
if (items == null || items.length < 1) {
return;
}
String line = items[0].getText(0);
if (line == null || "".equals(line)) { //$NON-NLS-1$
return;
}
Integer valueOf = Integer.valueOf(line);
if (valueOf != null) {
updateVerifierControl4insn(valueOf.intValue());
tableControl.setSelection(selectionIndex);
}
}
};
linkWithEditorAction = new DefaultToggleAction(BCOConstants.LINK_VIEW_TO_EDITOR) {
@Override
public void run(boolean newState) {
setMode(BCOConstants.F_LINK_VIEW_TO_EDITOR, newState);
if (modes.get(BCOConstants.F_LINK_VIEW_TO_EDITOR)) {
showSelectedOnlyAction.setEnabled(true);
toggleVerifierAction.setEnabled(true);
hideLineInfoAction.setEnabled(true);
hideLocalsAction.setEnabled(true);
toggleASMifierModeAction.setEnabled(true);
if (!toggleASMifierModeAction.isChecked()) {
setRawModeAction.setEnabled(true);
}
activateView();
checkOpenEditors(true);
inputChanged = true;
refreshView();
}
}
};
showSelectedOnlyAction = new DefaultToggleAction(BCOConstants.SHOW_ONLY_SELECTED_ELEMENT) {
@Override
public void run(boolean newState) {
setMode(BCOConstants.F_SHOW_ONLY_SELECTED_ELEMENT, newState);
inputChanged = true;
refreshView();
}
};
setRawModeAction = new DefaultToggleAction(BCOConstants.SHOW_RAW_BYTECODE) {
@Override
public void run(boolean newState) {
setMode(BCOConstants.F_SHOW_RAW_BYTECODE, newState);
inputChanged = true;
refreshView();
}
};
hideLineInfoAction = new DefaultToggleAction(BCOConstants.SHOW_LINE_INFO) {
@Override
public void run(boolean newState) {
setMode(BCOConstants.F_SHOW_LINE_INFO, newState);
inputChanged = true;
refreshView();
}
};
hideLocalsAction = new DefaultToggleAction(BCOConstants.SHOW_VARIABLES) {
@Override
public void run(boolean newState) {
setMode(BCOConstants.F_SHOW_VARIABLES, newState);
inputChanged = true;
refreshView();
}
};
hideStackMapAction = new DefaultToggleAction(BCOConstants.SHOW_STACKMAP) {
@Override
public void run(boolean newState) {
setMode(BCOConstants.F_SHOW_STACKMAP, newState);
inputChanged = true;
refreshView();
}
};
expandStackMapAction = new DefaultToggleAction(BCOConstants.EXPAND_STACKMAP) {
@Override
public void run(boolean newState) {
setMode(BCOConstants.F_EXPAND_STACKMAP, newState);
inputChanged = true;
refreshView();
}
};
showHexValuesAction = new DefaultToggleAction(BCOConstants.SHOW_HEX_VALUES) {
@Override
public void run(boolean newState) {
setMode(BCOConstants.F_SHOW_HEX_VALUES, newState);
inputChanged = true;
refreshView();
}
};
toggleASMifierModeAction = new DefaultToggleAction(BCOConstants.SHOW_ASMIFIER_CODE) {
@Override
public void run(boolean newState) {
setMode(BCOConstants.F_SHOW_ASMIFIER_CODE, newState);
if (newState) {
setMode(BCOConstants.F_SHOW_RAW_BYTECODE, true);
setRawModeAction.setEnabled(false);
} else {
setRawModeAction.setEnabled(true);
}
inputChanged = true;
refreshView();
}
};
toggleVerifierAction = new DefaultToggleAction(BCOConstants.SHOW_ANALYZER) {
@Override
public void run(boolean newState) {
toggleVerifyMode(mmanager, newState);
inputChanged = true;
refreshView();
}
};
mmanager.add(linkWithEditorAction);
mmanager.add(showSelectedOnlyAction);
mmanager.add(setRawModeAction);
mmanager.add(hideLineInfoAction);
mmanager.add(hideLocalsAction);
mmanager.add(showHexValuesAction);
mmanager.add(hideStackMapAction);
mmanager.add(expandStackMapAction);
mmanager.add(toggleASMifierModeAction);
mmanager.add(toggleVerifierAction);
mmanager.add(new Separator());
toggleOrientationActions = new ToggleOrientationAction[] {
new ToggleOrientationAction(VIEW_ORIENTATION_VERTICAL),
new ToggleOrientationAction(VIEW_ORIENTATION_HORIZONTAL),
new ToggleOrientationAction(VIEW_ORIENTATION_AUTOMATIC) };
for (ToggleOrientationAction toggleOrientationAction : toggleOrientationActions) {
mmanager.add(toggleOrientationAction);
}
tmanager.add(linkWithEditorAction);
tmanager.add(showSelectedOnlyAction);
tmanager.add(setRawModeAction);
// tmanager.add(hideLineInfoAction);
// tmanager.add(hideLocalsAction);
tmanager.add(toggleASMifierModeAction);
tmanager.add(toggleVerifierAction);
}
@SuppressWarnings("unused")
private void createVerifyControl() {
verifyControl = new SashForm(stackComposite, SWT.VERTICAL);
tableControl = new Table(verifyControl, SWT.SINGLE | SWT.FULL_SELECTION);
tableControlViewer = new TableViewer(tableControl);
TableColumn tc = new TableColumn(tableControl, SWT.LEFT);
tc.setText("#"); //$NON-NLS-1$
tc.setToolTipText("ASM instruction offset"); //$NON-NLS-1$
tc = new TableColumn(tableControl, SWT.LEFT);
tc.setText(Messages.BytecodeOutlineView_lvt_header);
tc.setToolTipText("Local variables"); //$NON-NLS-1$
tc = new TableColumn(tableControl, SWT.LEFT);
tc.setText(Messages.BytecodeOutlineView_stack_header);
tc.setToolTipText("Stack content *before* current instruction is executed"); //$NON-NLS-1$
new TableColumn(tableControl, SWT.LEFT);
new TableColumn(tableControl, SWT.LEFT);
tableControl.setLinesVisible(false);
tableControl.setHeaderVisible(true);
stackAndLvt = new SashForm(verifyControl, SWT.HORIZONTAL);
lvtTable = new Table(stackAndLvt, SWT.SINGLE | SWT.FULL_SELECTION);
lvtTable.setLinesVisible(false);
lvtTable.setHeaderVisible(true);
new TableColumn(lvtTable, SWT.LEFT).setText("#"); //$NON-NLS-1$
new TableColumn(lvtTable, SWT.LEFT).setText("Var Type"); //$NON-NLS-1$
new TableColumn(lvtTable, SWT.LEFT).setText("Name"); //$NON-NLS-1$
stackTable = new Table(stackAndLvt, SWT.SINGLE | SWT.FULL_SELECTION);
stackTable.setLinesVisible(false);
stackTable.setHeaderVisible(true);
new TableColumn(stackTable, SWT.LEFT).setText("#"); //$NON-NLS-1$
new TableColumn(stackTable, SWT.LEFT).setText("Stack Type"); //$NON-NLS-1$
stackAndLvt.setWeights(50, 50);
verifyControl.setWeights(75, 25);
tableControl.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
if (modes.get(BCOConstants.F_LINK_VIEW_TO_EDITOR)) {
selectionChangedAction.run();
}
refreshVarsAndStackAction.run();
}
});
}
private void createSelectionProvider() {
viewSelectionProvider = new BCOViewSelectionProvider();
viewSelectionProvider.registerSelectionProvider(textViewer);
viewSelectionProvider.registerSelectionProvider(tableControlViewer);
if (modes.get(BCOConstants.F_SHOW_ANALYZER)) {
viewSelectionProvider.setCurrentSelectionProvider(tableControlViewer);
} else {
viewSelectionProvider.setCurrentSelectionProvider(textViewer);
}
getSite().setSelectionProvider(viewSelectionProvider);
}
/**
* create/register context menu on text control
*/
private void createTextContextMenu() {
String id = "org.eclipse.jdt.bcoview.views.BytecodeOutlineView#ContextMenu"; //$NON-NLS-1$
contextMenuManager = new MenuManager("#ContextMenu", id); //$NON-NLS-1$
contextMenuManager.setRemoveAllWhenShown(true);
contextMenuManager.addMenuListener(this::contextMenuAboutToShow);
Menu menu = contextMenuManager.createContextMenu(textControl);
textControl.setMenu(menu);
getSite().registerContextMenu(id, contextMenuManager, textViewer);
}
private void createTextControl() {
IPreferenceStore store = JavaPlugin.getDefault().getCombinedPreferenceStore();
final JavaSourceViewer viewer = new JavaSourceViewer(stackComposite, null, null, true, SWT.V_SCROLL | SWT.H_SCROLL, store);
IColorManager colorManager = JavaPlugin.getDefault().getJavaTextTools().getColorManager();
JavaSourceViewerConfiguration configuration = new JavaConfiguration(colorManager, store, null, IJavaPartitions.JAVA_PARTITIONING);
viewer.configure(configuration);
viewer.setEditable(false);
textViewer = viewer;
textControl = textViewer.getTextWidget();
IDocument document = new Document(""); //$NON-NLS-1$
textViewer.setDocument(document);
textSelectionListener = event -> {
for (String selectionAction : selectionActions) {
updateAction(selectionAction);
}
};
textListener = event -> {
IUpdate findReplace = (IUpdate) globalActions.get(ActionFactory.FIND.getId());
if (findReplace != null) {
findReplace.update();
}
};
textViewer.getSelectionProvider().addSelectionChangedListener(textSelectionListener);
textViewer.addTextListener(textListener);
textControl.addMouseListener(new MouseAdapter() {
@Override
public void mouseDown(MouseEvent e) {
if (modes.get(BCOConstants.F_LINK_VIEW_TO_EDITOR)) {
selectionChangedAction.run();
}
}
@Override
public void mouseUp(MouseEvent e) {
if (modes.get(BCOConstants.F_LINK_VIEW_TO_EDITOR)) {
selectionChangedAction.run();
}
}
});
textControl.addKeyListener(new KeyListener() {
@Override
public void keyPressed(KeyEvent e) {
// ignored
}
@Override
public void keyReleased(KeyEvent e) {
if (modes.get(BCOConstants.F_LINK_VIEW_TO_EDITOR)) {
selectionChangedAction.run();
}
}
});
}
@Override
public void dispose() {
deActivateView();
if (editorListener != null) {
IWorkbenchWindow workbenchWindow = getSite().getWorkbenchWindow();
workbenchWindow.getPartService().removePartListener(editorListener);
workbenchWindow.getSelectionService().removePostSelectionListener(editorListener);
FileBuffers.getTextFileBufferManager().removeFileBufferListener(editorListener);
editorListener.dispose();
editorListener = null;
}
if (contextMenuManager != null) {
contextMenuManager.dispose();
}
selectionActions.clear();
globalActions.clear();
textViewer.getSelectionProvider().removeSelectionChangedListener(textSelectionListener);
textViewer.removeTextListener(textListener);
textViewer = null;
viewSelectionProvider = null;
if (textControl != null) {
textControl.dispose();
textControl = null;
}
if (verifyControl != null) {
verifyControl.dispose();
verifyControl = null;
tableControl = null;
stackTable = null;
lvtTable = null;
tableControlViewer = null;
}
currentSelection = null;
javaEditor = null;
setJavaInput(null);
lastChildElement = null;
lastDecompiledResult = null;
linkWithEditorAction.dispose();
showSelectedOnlyAction.dispose();
setRawModeAction.dispose();
toggleASMifierModeAction.dispose();
hideLineInfoAction.dispose();
hideLocalsAction.dispose();
hideStackMapAction.dispose();
showHexValuesAction.dispose();
expandStackMapAction.dispose();
toggleVerifierAction.dispose();
linkWithEditorAction = null;
selectionChangedAction = null;
refreshVarsAndStackAction = null;
showSelectedOnlyAction = null;
setRawModeAction = null;
toggleASMifierModeAction = null;
hideLineInfoAction = null;
hideLocalsAction = null;
hideStackMapAction = null;
showHexValuesAction = null;
expandStackMapAction = null;
toggleVerifierAction = null;
super.dispose();
}
protected void contextMenuAboutToShow(IMenuManager menuManager) {
IDocument doc = textViewer.getDocument();
if (doc == null) {
return;
}
menuManager.add(globalActions.get(ActionFactory.COPY.getId()));
menuManager.add(globalActions.get(ActionFactory.SELECT_ALL.getId()));
menuManager.add(new Separator("FIND")); //$NON-NLS-1$
menuManager.add(globalActions.get(ActionFactory.FIND.getId()));
menuManager.add(new Separator(IWorkbenchActionConstants.MB_ADDITIONS));
}
@Override
public void setFocus() {
if (!modes.get(BCOConstants.F_SHOW_ANALYZER)) {
if (textViewer != null) {
textViewer.getTextWidget().setFocus();
}
} else {
if (tableControl != null) {
tableControl.setFocus();
}
}
}
protected void handleBufferIsDirty(boolean isDirty) {
if (!modes.get(BCOConstants.F_LINK_VIEW_TO_EDITOR) || !isActive()) {
return;
}
if (isDirty) {
setBufferIsDirty(isDirty);
} else {
if (!bufferIsDirty) {
// second time calling with same argument -
// cause new bytecode should be written now
inputChanged = true;
refreshView();
} else {
// first time - set the flag only - cause
// bytecode is not yet written
setBufferIsDirty(false);
}
}
}
protected void handlePartHidden(IWorkbenchPart part) {
if (!modes.get(BCOConstants.F_LINK_VIEW_TO_EDITOR)) {
return;
}
if (this == part) {
isVisible = false;
deActivateView();
} else if (isActive() && (part instanceof IEditorPart)) {
// check if at least one editor is open
checkOpenEditors(false);
}
}
protected void handlePartVisible(IWorkbenchPart part) {
if (!modes.get(BCOConstants.F_LINK_VIEW_TO_EDITOR)) {
if (this == part) {
isVisible = true;
}
return;
}
if (this == part) {
if (isVisible) {
return;
}
isVisible = true;
// check if java editor is already open
IEditorPart activeEditor = EclipseUtils.getActiveEditor();
if (!(activeEditor instanceof ITextEditor)) {
// start monitoring again, even if current editor is not
// supported - but we at front now
activateView();
return;
}
part = activeEditor;
// continue with setting input
}
if (isVisible && part instanceof ITextEditor) {
if (isActive() && part == javaEditor) {
return;
}
activateView();
setEnabled(true);
setInput((ITextEditor) part);
refreshView();
} else if (part instanceof IEditorPart) {
if (isActive()) {
deActivateView();
}
}
}
protected void handleSelectionChanged(IWorkbenchPart part, ISelection selection) {
if (!modes.get(BCOConstants.F_LINK_VIEW_TO_EDITOR) || !isActive() || !isVisible || !(part instanceof IEditorPart)) {
return;
}
if (!(part instanceof ITextEditor)) {
deActivateView();
return;
}
if (!isEnabled()) {
setEnabled(true);
}
if (part != javaEditor) {
setInput((ITextEditor) part);
} else {
if (!updateSelection((ITextSelection) selection)) {
return;
}
}
refreshView();
}
/**
* Does nothing if view is already active
*/
private void activateView() {
if (isActive()) {
return;
}
isActive = true;
getSite().getWorkbenchWindow().getSelectionService().addPostSelectionListener(editorListener);
FileBuffers.getTextFileBufferManager().addFileBufferListener(editorListener);
}
/**
* Does nothing if view is already deactivated
*/
private void deActivateView() {
if (!isActive()) {
return;
}
setEnabled(false);
if (editorListener != null) {
ISelectionService service = getSite().getWorkbenchWindow().getSelectionService();
if (service != null) {
service.removePostSelectionListener(editorListener);
}
FileBuffers.getTextFileBufferManager().removeFileBufferListener(editorListener);
}
if (textViewer != null && textViewer.getTextWidget() != null && !textViewer.getTextWidget().isDisposed()) {
IDocument document = new Document(""); //$NON-NLS-1$
textViewer.setDocument(document);
}
if (tableControl != null && !tableControl.isDisposed()) {
setVerifyTableItems(null);
}
if (stackTable != null && !stackTable.isDisposed()) {
stackTable.removeAll();
}
if (lvtTable != null && !lvtTable.isDisposed()) {
lvtTable.removeAll();
}
if (statusControl != null && !statusControl.isDisposed()) {
updateStatus(null, -1, -1);
}
currentSelection = null;
lastDecompiledResult = null;
javaEditor = null;
setJavaInput(null);
lastChildElement = null;
setBufferIsDirty(false);
isActive = false;
}
protected void refreshView() {
if (!isActive()) {
return;
}
IJavaElement childEl = getCurrentJavaElement();
if (childEl == null && javaInput == null) {
setInput(javaEditor);
childEl = javaInput;
}
// after getCurrentJavaElement() call it is possible that java type is disappear
// because corresponding type is not more exist in model
if (javaInput == null) {
deActivateView();
return;
}
boolean clearOutput = false;
if (inputChanged || isSelectedElementChanged(childEl)) {
DecompiledClass result = decompileBytecode(childEl);
if (result == null) {
clearOutput = true;
} else {
boolean hasMethods = !result.isAbstractOrInterface() || result.isDefaultMethodPossible();
if (modes.get(BCOConstants.F_SHOW_ANALYZER) && hasMethods) {
refreshVerifyView(result);
} else {
toggleVerifierAction.setEnabled(hasMethods);
refreshTextView(result);
}
}
lastDecompiledResult = result;
} else if (childEl == null && modes.get(BCOConstants.F_SHOW_ONLY_SELECTED_ELEMENT)) {
clearOutput = true;
}
lastChildElement = childEl;
if (clearOutput) {
if (!modes.get(BCOConstants.F_SHOW_ANALYZER)) {
IDocument document = new Document(""); //$NON-NLS-1$
textViewer.setDocument(document);
} else {
setVerifyTableItems(null);
}
}
setSelectionInBytecodeView();
inputChanged = false;
}
private void refreshTextView(DecompiledClass result) {
IDocument document = new Document(result.getText());
JavaTextTools tools = JavaPlugin.getDefault().getJavaTextTools();
tools.setupJavaDocumentPartitioner(document, IJavaPartitions.JAVA_PARTITIONING);
textViewer.setDocument(document);
// we are in verify mode but we can't show content because
// current element is abstract, so we clean table content
if (modes.get(BCOConstants.F_SHOW_ANALYZER)) {
setVerifyTableItems(null);
}
hasAnalyzerError = false;
}
private void refreshVerifyView(DecompiledClass result) {
setVerifyTableItems(result.getTextTable());
List errors = result.getErrorLines();
if (errors.size() > 0) {
// TODO this only changes color of status line -
// but it is possible also to provide useful info here...
hasAnalyzerError = true;
// currentErrorMessage = ...
}
for (Integer error : errors) {
int l = error.intValue();
tableControl.getItem(l).setForeground(errorColor);
}
toggleVerifierAction.setEnabled(true);
}
private void updateStatus(DecompiledClass result, int bytecodeOffsetStart, int bytecodeOffsetEnd) {
// clear error messages, if any
statusLineManager.setErrorMessage(null);
if (result != null) {
currentStatusMessage = "Java:" //$NON-NLS-1$
+ result.getJavaVersion() + " | class size:" //$NON-NLS-1$
+ result.getClassSize();
ClassNode classNode = result.getClassNode();
if (classNode != null && classNode.name != null) {
setContentDescription(classNode.name);
}
} else {
currentStatusMessage = ""; //$NON-NLS-1$
setContentDescription(""); //$NON-NLS-1$
}
String selectionInfo = ""; //$NON-NLS-1$
if (bytecodeOffsetStart >= 0) {
selectionInfo = " | offset:" + bytecodeOffsetStart; //$NON-NLS-1$
if (bytecodeOffsetEnd >= 0) {
selectionInfo += "-" + bytecodeOffsetEnd; //$NON-NLS-1$
}
}
if (hasAnalyzerError) {
statusLineManager.setErrorMessage(currentStatusMessage + selectionInfo);
} else {
statusLineManager.setMessage(currentStatusMessage + selectionInfo);
}
}
@Override
public int getBytecodeInstructionAtLine(int line) {
if (lastDecompiledResult != null) {
return lastDecompiledResult.getBytecodeInsn(line);
}
return -1;
}
/**
* @return IJavaElement which fits in the current selection in java editor
*/
private IJavaElement getCurrentJavaElement() {
IJavaElement childEl = null;
try {
childEl = JdtUtils.getElementAtOffset(javaInput, currentSelection);
if (childEl != null) {
switch (childEl.getElementType()) {
case IJavaElement.METHOD:
case IJavaElement.FIELD:
case IJavaElement.INITIALIZER:
case IJavaElement.TYPE:
break;
case IJavaElement.LOCAL_VARIABLE:
childEl = childEl.getAncestor(IJavaElement.METHOD);
break;
default:
childEl = null;
break;
}
}
} catch (JavaModelException e) {
// the exception is mostly occured if java structure was
// changed and current element is not more exist in model
// e.g. on rename/delete/move operation.
// so it is not an error for user, but info for us
BytecodeOutlinePlugin.log(e, IStatus.INFO);
setJavaInput(null);
lastChildElement = null;
}
return childEl;
}
private void setSelectionInBytecodeView() {
if (lastDecompiledResult == null) {
return;
}
if (currentSelection.getStartLine() != currentSelection.getEndLine()) {
setMultiLineSelectionInBytecodeView(currentSelection);
return;
}
int sourceLine = currentSelection.getStartLine() + 1;
int decompiledLine = lastDecompiledResult.getDecompiledLine(sourceLine);
if (decompiledLine < 0
&& !modes.get(BCOConstants.F_SHOW_ONLY_SELECTED_ELEMENT)
&& lastChildElement != null) {
/*
* May be this is the selection in outline view, if complete class is shown.
* Because there are no bytecode instructions/offset for method name, we need
* to find and select first method line. See cr 306011
*/
DecompiledMethod match = lastDecompiledResult.getBestDecompiledMatch(sourceLine);
if (match != null) {
// this is relative to method start
decompiledLine = match.getBestDecompiledLine(sourceLine);
if (decompiledLine > 0) {
// convert to class file relative
decompiledLine = lastDecompiledResult.getDecompiledLine(match, decompiledLine);
}
}
if (decompiledLine < 0) {
String methodName = JdtUtils.getMethodSignature(lastChildElement);
if (methodName != null) {
decompiledLine = lastDecompiledResult.getDecompiledLine(methodName) - 1;
}
}
}
if (decompiledLine > 0) {
try {
if (modes.get(BCOConstants.F_SHOW_ANALYZER)) {
updateVerifierControl4line(decompiledLine);
tableControl.setSelection(decompiledLine);
} else {
int lineCount = textControl.getLineCount();
if (decompiledLine < lineCount) {
int offsetAtLine = textControl.getOffsetAtLine(decompiledLine);
int offsetEnd = textControl.getText().indexOf('\n', offsetAtLine);
textControl.setSelection(offsetAtLine, offsetEnd);
}
}
} catch (IllegalArgumentException e) {
BytecodeOutlinePlugin.error(null, e);
}
} else if (modes.get(BCOConstants.F_SHOW_ANALYZER)) {
lvtTable.removeAll();
stackTable.removeAll();
}
int bytecodeOffset = lastDecompiledResult.getBytecodeOffset(decompiledLine);
updateStatus(lastDecompiledResult, bytecodeOffset, -1);
}
private void setMultiLineSelectionInBytecodeView(ITextSelection multiLineSelection) {
LineRange range = lastDecompiledResult.getDecompiledRange(multiLineSelection);
int firstDecompiledLine = range.startLine;
if (firstDecompiledLine > 0) {
try {
if (modes.get(BCOConstants.F_SHOW_ANALYZER)) {
updateVerifierControl4line(firstDecompiledLine);
tableControl.setSelection(firstDecompiledLine);
} else {
int lineCount = textControl.getLineCount();
if (firstDecompiledLine < lineCount) {
int offsetAtLine = textControl.getOffsetAtLine(firstDecompiledLine);
int offsetEnd;
String text = textControl.getText();
if (range.endLine > 0 && range.endLine < lineCount) {
offsetEnd = textControl.getOffsetAtLine(range.endLine);
offsetEnd = text.indexOf("LINENUMBER", text.indexOf('\n', offsetEnd)); //$NON-NLS-1$
if (offsetEnd < 0) {
offsetEnd = text.indexOf('\n', offsetEnd);
}
} else {
offsetEnd = text.indexOf('\n', offsetAtLine);
}
textControl.setSelection(offsetAtLine, offsetEnd);
}
}
} catch (IllegalArgumentException e) {
BytecodeOutlinePlugin.error(null, e);
}
} else if (modes.get(BCOConstants.F_SHOW_ANALYZER)) {
lvtTable.removeAll();
stackTable.removeAll();
}
int bytecodeOffsetStart = lastDecompiledResult.getBytecodeOffset(firstDecompiledLine);
int bytecodeOffsetEnd = lastDecompiledResult.getBytecodeOffset(range.endLine);
updateStatus(lastDecompiledResult, bytecodeOffsetStart, bytecodeOffsetEnd);
}
protected void updateVerifierControl4line(int decompiledLine) {
String[][][] frame = lastDecompiledResult.getFrameTables(decompiledLine, !modes.get(BCOConstants.F_SHOW_RAW_BYTECODE));
updateVerifierControl(frame);
}
protected void updateVerifierControl4insn(int insn) {
String[][][] frame = lastDecompiledResult.getFrameTablesForInsn(insn, !modes.get(BCOConstants.F_SHOW_RAW_BYTECODE));
updateVerifierControl(frame);
}
private void updateVerifierControl(String[][][] frame) {
lvtTable.removeAll();
stackTable.removeAll();
if (frame == null) {
return;
}
for (int i = 0; i < frame[0].length; ++i) {
if (frame[0][i] != null) {
new TableItem(lvtTable, SWT.NONE).setText(frame[0][i]);
}
}
for (int i = 0; i < frame[1].length; ++i) {
if (frame[1][i] != null) {
new TableItem(stackTable, SWT.NONE).setText(frame[1][i]);
}
}
lvtTable.getColumn(0).pack();
lvtTable.getColumn(1).pack();
lvtTable.getColumn(2).pack();
stackTable.getColumn(0).pack();
stackTable.getColumn(1).pack();
}
protected void setSelectionInJavaEditor(Point selection) {
if (javaEditor != null && javaEditor.getEditorInput() == null) {
// editor was closed - we should clean the reference
javaEditor = null;
setJavaInput(null);
}
if (javaEditor == null || lastDecompiledResult == null) {
deActivateView();
return;
}
int startDecLine;
int endDecLine = -1;
if (modes.get(BCOConstants.F_SHOW_ANALYZER)) {
startDecLine = tableControl.getSelectionIndex();
endDecLine = startDecLine;
} else {
startDecLine = textControl.getLineAtOffset(selection.x);
endDecLine = textControl.getLineAtOffset(selection.y);
}
int startSourceLine = lastDecompiledResult.getSourceLine(startDecLine);
int endSourceLine = -1;
if (endDecLine > 0) {
endSourceLine = lastDecompiledResult.getSourceLine(endDecLine);
}
if (endSourceLine < startSourceLine) {
int tmp = startSourceLine;
startSourceLine = endSourceLine;
endSourceLine = tmp;
}
try {
if (startSourceLine > 0) {
IDocument document = javaEditor.getDocumentProvider().getDocument(javaEditor.getEditorInput());
try {
IRegion lineInfo = document.getLineInformation(startSourceLine - 1);
int startOffset = lineInfo.getOffset();
int length = lineInfo.getLength();
if (endSourceLine > 0) {
IRegion region = document.getLineInformation(endSourceLine - 1);
length = region.getLength() + (region.getOffset() - startOffset);
}
EclipseUtils.selectInEditor(javaEditor, startOffset, length);
} catch (BadLocationException e) {
// do nothing. This could happens e.g. if editor does not contain
// full source code etc, so that line info is not exist in editor
}
}
} catch (Exception e) {
BytecodeOutlinePlugin.log(e, IStatus.ERROR);
}
int bytecodeOffset = lastDecompiledResult.getBytecodeOffset(startDecLine);
updateStatus(lastDecompiledResult, bytecodeOffset, -1);
}
/**
* check if at least one java editor is open - if not, deactivate me
*
* @param checkNewSelection check selection in active editor
*/
protected void checkOpenEditors(boolean checkNewSelection) {
IEditorReference[] editorReferences = getSite().getPage().getEditorReferences();
if (editorReferences == null || editorReferences.length == 0) {
deActivateView();
} else if (checkNewSelection) {
IEditorPart activeEditor = EclipseUtils.getActiveEditor();
if (activeEditor instanceof ITextEditor) {
ITextSelection selection = EclipseUtils.getSelection(((ITextEditor) activeEditor).getSelectionProvider());
handleSelectionChanged(activeEditor, selection);
} else {
deActivateView();
}
}
}
/**
* @param childEl can be null
* @return true if java element selection was changed (means, that previous selection do not
* match to the given element)
*/
private boolean isSelectedElementChanged(IJavaElement childEl) {
if (lastChildElement == null && childEl == null) {
// no selected child before - and no new selection now => no changes
return false;
}
if (modes.get(BCOConstants.F_SHOW_ONLY_SELECTED_ELEMENT)) {
if (lastChildElement == null || !lastChildElement.equals(childEl)) {
return true;
}
}
/*
* the check if we changed from inner class to outer class or vice versa
*/
if (lastChildElement != null && childEl != null) {
IType newEnclosingType = JdtUtils.getEnclosingType(childEl);
IType oldEnclosingType = JdtUtils.getEnclosingType(lastChildElement);
return newEnclosingType == null || !newEnclosingType.equals(oldEnclosingType);
}
return false;
}
/**
* @param childEl can be null
* @return return null if type is not known or bytecode is not written or cannot be found
*/
private DecompiledClass decompileBytecode(IJavaElement childEl) {
// check here for inner classes too
IJavaElement type = JdtUtils.getEnclosingType(childEl);
if (type == null) {
type = javaInput;
}
if (type == null) {
return null;
}
byte[] bytes = JdtUtils.readClassBytes(type);
if (bytes == null) {
return null;
}
DecompiledClass decompiledClass = null;
int available = bytes.length;
try {
String fieldName = null;
String methodName = null;
/*
* find out, which name we should use for selected element
*/
if (modes.get(BCOConstants.F_SHOW_ONLY_SELECTED_ELEMENT) && childEl != null) {
if (childEl.getElementType() == IJavaElement.FIELD) {
fieldName = childEl.getElementName();
} else {
methodName = JdtUtils.getMethodSignature(childEl);
}
}
decompiledClass = DecompilerHelper.getDecompiledClass(bytes, new DecompilerOptions(fieldName, methodName, modes));
} catch (Exception e) {
try {
// check if compilation unit is ok - then this is the user problem
if (type.isStructureKnown()) {
BytecodeOutlinePlugin.error("Cannot decompile: " + type, e); //$NON-NLS-1$
} else {
BytecodeOutlinePlugin.log(e, IStatus.ERROR);
}
} catch (JavaModelException e1) {
// this is compilation problem - don't show the message
BytecodeOutlinePlugin.log(e1, IStatus.WARNING);
}
} catch (UnsupportedClassVersionError e) {
BytecodeOutlinePlugin.error("Cannot decompile: " + type //$NON-NLS-1$
+ ". Error was caused by attempt to " //$NON-NLS-1$
+ "load a class compiled with the Java version which is not " //$NON-NLS-1$
+ "supported by the current JVM. ", e); //$NON-NLS-1$
}
// remember class file size to show it later in UI
if (decompiledClass != null) {
decompiledClass.setClassSize(available);
}
return decompiledClass;
}
private void setVerifyTableItems(String[][] items) {
tableControl.removeAll();
if (items != null) {
for (int i = 0; i < items.length; ++i) {
TableItem item = new TableItem(tableControl, SWT.NONE);
for (int j = 0; j < items[i].length; ++j) {
String s = items[i][j];
if (s.endsWith("\n")) { //$NON-NLS-1$
s = s.substring(0, s.length() - 1);
// this is the "cookie" for the bytecode reference, which could be
// mapped later to the sourcecode line on selection event in the
// table
item.setData(Integer.valueOf(i));
}
item.setText(j, s);
}
}
tableControl.getColumn(0).pack();
tableControl.getColumn(1).pack();
tableControl.getColumn(2).pack();
tableControl.getColumn(3).pack();
tableControl.getColumn(4).pack();
}
}
@Override
public T getAdapter(Class adapter) {
if (IFindReplaceTarget.class.equals(adapter)) {
return adapter.cast(textViewer.getFindReplaceTarget());
}
if (Widget.class.equals(adapter)) {
return adapter.cast(textViewer.getTextWidget());
}
if (TextViewer.class.equals(adapter)) {
return adapter.cast(textViewer);
}
return super.getAdapter(adapter);
}
/**
* Configures an action for key bindings.
*
* @param actionBars action bars for this page
* @param actionID action definition id
* @param action associated action
*/
protected void setGlobalAction(IActionBars actionBars, String actionID, IAction action) {
globalActions.put(actionID, action);
actionBars.setGlobalActionHandler(actionID, action);
}
/**
* Updates the global action with the given id
*
* @param actionId action definition id
*/
protected void updateAction(String actionId) {
IAction action = globalActions.get(actionId);
if (action instanceof IUpdate) {
((IUpdate) action).update();
}
}
protected void createTextActions() {
IActionBars actionBars = getViewSite().getActionBars();
TextViewerAction action = new TextViewerAction(textViewer, ITextOperationTarget.SELECT_ALL);
action.configureAction(
Messages.BytecodeOutlineView_select_all_label,
Messages.BytecodeOutlineView_select_all_tooltip,
Messages.BytecodeOutlineView_select_all_description);
setGlobalAction(actionBars, ActionFactory.SELECT_ALL.getId(), action);
action = new TextViewerAction(textViewer, ITextOperationTarget.COPY);
action.configureAction(
Messages.BytecodeOutlineView_copy_label,
Messages.BytecodeOutlineView_copy_tooltip,
Messages.BytecodeOutlineView_copy_description);
action.setImageDescriptor(PlatformUI.getWorkbench().getSharedImages().getImageDescriptor(ISharedImages.IMG_TOOL_COPY));
action.setActionDefinitionId(IWorkbenchCommandConstants.EDIT_COPY);
setGlobalAction(actionBars, ActionFactory.COPY.getId(), action);
ResourceBundle bundle = Messages.getResourceBundle();
setGlobalAction(actionBars, ActionFactory.FIND.getId(), new FindReplaceAction(bundle, "BytecodeOutlineView_find_replace_", this)); //$NON-NLS-1$
selectionActions.add(ActionFactory.COPY.getId());
selectionActions.add(ActionFactory.FIND.getId());
actionBars.updateActionBars();
}
private void setOrientation(int orientation) {
if (verifyControl == null || verifyControl.isDisposed()) {
return;
}
boolean horizontal = orientation == VIEW_ORIENTATION_HORIZONTAL;
verifyControl.setOrientation(horizontal ? SWT.HORIZONTAL : SWT.VERTICAL);
for (ToggleOrientationAction toggleOrientationAction : toggleOrientationActions) {
toggleOrientationAction.setChecked(orientation == toggleOrientationAction.getOrientation());
}
currentOrientation = orientation;
stackComposite.getParent().layout();
}
protected void computeOrientation() {
if (orientation != VIEW_ORIENTATION_AUTOMATIC) {
currentOrientation = orientation;
setOrientation(currentOrientation);
} else {
Point size = stackComposite.getParent().getSize();
if (size.x != 0 && size.y != 0) {
setOrientation(size.x > size.y ? VIEW_ORIENTATION_HORIZONTAL : VIEW_ORIENTATION_VERTICAL);
}
}
}
/**
* Set the bit with given index to given value and remembers it in the preferences
*
* @param bitIndex one of BCOConstants.F_* constants
* @param value flag
*/
protected void setMode(int bitIndex, boolean value) {
modes.set(bitIndex, value);
}
protected void toggleVerifyMode(final IMenuManager mmanager, boolean showAnalyzer) {
setMode(BCOConstants.F_SHOW_ANALYZER, showAnalyzer);
if (modes.get(BCOConstants.F_SHOW_ANALYZER)) {
((StackLayout) stackComposite.getLayout()).topControl = verifyControl;
viewSelectionProvider.setCurrentSelectionProvider(tableControlViewer);
} else {
((StackLayout) stackComposite.getLayout()).topControl = textControl;
viewSelectionProvider.setCurrentSelectionProvider(textViewer);
}
stackComposite.layout();
for (ToggleOrientationAction toggleOrientationAction : toggleOrientationActions) {
toggleOrientationAction.setEnabled(showAnalyzer);
}
mmanager.markDirty();
mmanager.update();
}
private class ToggleOrientationAction extends Action {
private final int actionOrientation;
public ToggleOrientationAction(int orientation) {
super("", AS_RADIO_BUTTON); //$NON-NLS-1$
String symbolicName = BytecodeOutlinePlugin.getDefault().getBundle().getSymbolicName();
if (orientation == VIEW_ORIENTATION_HORIZONTAL) {
setText(Messages.BytecodeOutlineView_toggle_horizontal_label);
setImageDescriptor(AbstractUIPlugin.imageDescriptorFromPlugin(symbolicName, "icons/th_horizontal.gif")); //$NON-NLS-1$
} else if (orientation == VIEW_ORIENTATION_VERTICAL) {
setText(Messages.BytecodeOutlineView_toggle_vertical_label);
setImageDescriptor(AbstractUIPlugin.imageDescriptorFromPlugin(symbolicName, "icons/th_vertical.gif")); //$NON-NLS-1$
} else if (orientation == VIEW_ORIENTATION_AUTOMATIC) {
setText(Messages.BytecodeOutlineView_toggle_automatic_label);
setImageDescriptor(AbstractUIPlugin.imageDescriptorFromPlugin(symbolicName, "icons/th_automatic.gif")); //$NON-NLS-1$
}
actionOrientation = orientation;
}
public int getOrientation() {
return actionOrientation;
}
@Override
public void run() {
if (isChecked()) {
orientation = actionOrientation;
computeOrientation();
}
}
}
protected IJavaElement[] guessTypesFromSelectionInView(IRegion wordRegion) throws JavaModelException {
if (wordRegion == null || wordRegion.getLength() == 0 || javaInput == null) {
return null;
}
String typeName;
try {
typeName = textViewer.getDocument().get(wordRegion.getOffset(), wordRegion.getLength());
} catch (BadLocationException e) {
return null;
}
if (typeName.isEmpty()) {
return null;
}
if (typeName.contains("$")) { //$NON-NLS-1$
typeName = typeName.substring(typeName.lastIndexOf('$') + 1);
}
IJavaSearchScope scope = SearchEngine.createJavaSearchScope(new IJavaElement[] { javaInput.getJavaProject() });
return JdtUtils.getTypeForName(typeName, scope, null);
}
private class JavaElementHyperlinkDetectorInView extends JavaElementHyperlinkDetector {
@Override
public IHyperlink[] detectHyperlinks(ITextViewer textViewer1, IRegion region, boolean canShowMultipleHyperlinks) {
if (region == null || javaInput == null) {
return null;
}
IAction openAction = new OpenAction(getSite());
int offset = region.getOffset();
IDocument document = textViewer1.getDocument();
IRegion wordRegion = JavaWordFinder.findWord(document, offset);
List links = new ArrayList<>();
IJavaElement[] elements;
try {
elements = guessTypesFromSelectionInView(wordRegion);
} catch (JavaModelException e) {
return null;
}
// TODO check for inner class files possibly referenced in current line.
// If found, add new hyperlink to jump to this inner class, see
// https://forge.ow2.org/tracker/index.php?func=detail&aid=316206&group_id=23&atid=350023
if (elements == null) {
return null;
}
elements = JdtUtils.selectOpenableElements(elements);
if (elements.length == 0) {
return null;
}
for (IJavaElement element : elements) {
if (element == null) {
continue;
}
addHyperlinks2(links, wordRegion, (SelectionDispatchAction) openAction, element, elements.length > 1);
}
if (links.size() == 0) {
return null;
}
return links.toArray(new IHyperlink[links.size()]);
}
/**
* This method is added for compatibility with Eclipse 3.6 and 3.7 only!
*
* Creates and adds Java element hyperlinks.
*
* @param hyperlinksCollector the list to which hyperlinks should be added
* @param wordRegion the region of the link
* @param openAction the action to use to open the Java elements
* @param element the Java element to open
* @param qualify true
if the hyperlink text should show a qualified name for
* element
*/
protected void addHyperlinks2(List hyperlinksCollector, IRegion wordRegion, SelectionDispatchAction openAction, IJavaElement element, boolean qualify) {
hyperlinksCollector.add(new JavaElementHyperlink(wordRegion, openAction, element, qualify));
}
}
private final class JavaConfiguration extends JavaSourceViewerConfiguration {
private JavaConfiguration(IColorManager colorManager, IPreferenceStore preferenceStore, ITextEditor editor, String partitioning) {
super(colorManager, preferenceStore, editor, partitioning);
}
@Override
public IHyperlinkDetector[] getHyperlinkDetectors(ISourceViewer sourceViewer) {
// does not work, as they work on *text editor*, not on the *view*...
// HyperlinkDetectorRegistry registry = EditorsUI.getHyperlinkDetectorRegistry();
// IHyperlinkDetector[] detectors = registry.createHyperlinkDetectors("org.eclipse.jdt.ui.javaCode", dummyEditorForHyperlinks);
JavaElementHyperlinkDetectorInView det = new JavaElementHyperlinkDetectorInView();
return new IHyperlinkDetector[] { det };
}
@Override
public ITextHover getTextHover(ISourceViewer sourceViewer, String contentType, int stateMask) {
return new JavadocHoverExtension();
}
@Override
public IQuickAssistAssistant getQuickAssistAssistant(ISourceViewer sourceViewer) {
return null;
}
}
private final class JavadocHoverExtension extends JavadocHover {
private final Set OPCODES = new HashSet<>(Arrays.asList(Printer.OPCODES));
@Override
protected IJavaElement[] getJavaElementsAt(ITextViewer textViewer1, IRegion hoverRegion) {
try {
return guessTypesFromSelectionInView(hoverRegion);
} catch (JavaModelException e) {
return null;
}
}
@Override
public Object getHoverInfo2(ITextViewer viewer, IRegion region) {
String typeName;
IDocument document = viewer.getDocument();
try {
typeName = document.get(region.getOffset(), region.getLength());
} catch (BadLocationException e) {
return null;
}
if (!OPCODES.contains(typeName)) {
return super.getHoverInfo2(viewer, region);
}
int line;
try {
line = document.getLineOfOffset(region.getOffset());
} catch (BadLocationException e) {
return null;
}
StringBuilder sb = HelpUtils.getOpcodeHelpFor(getBytecodeInstructionAtLine(line));
if (sb.length() > 0) {
return new JavadocBrowserInformationControlInput(null, null, sb.toString(), 0);
}
return null;
}
}
}