Java tutorial
/******************************************************************************* * Copyright (c) 2011 Andrey Loskutov. * 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 * * Contributor: Andrei Loskutov - initial API and implementation *******************************************************************************/ package de.loskutov.bco.views; import java.io.IOException; import java.io.InputStream; import java.util.ArrayList; import java.util.Arrays; import java.util.BitSet; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.ResourceBundle; import java.util.Set; import org.eclipse.core.filebuffers.FileBuffers; import org.eclipse.core.runtime.IStatus; 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.internal.ui.JavaPlugin; import org.eclipse.jdt.internal.ui.javaeditor.JavaEditor; 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; 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.jface.action.Action; import org.eclipse.jface.action.IAction; import org.eclipse.jface.action.IMenuListener; 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.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.TextEvent; 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.jface.viewers.ISelection; import org.eclipse.jface.viewers.ISelectionChangedListener; import org.eclipse.jface.viewers.SelectionChangedEvent; import org.eclipse.jface.viewers.TableViewer; 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.ui.IActionBars; import org.eclipse.ui.IEditorInput; 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.IWorkbenchPartSite; 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.objectweb.asm.tree.ClassNode; import org.objectweb.asm.util.Printer; import de.loskutov.bco.BytecodeOutlinePlugin; import de.loskutov.bco.asm.DecompiledClass; import de.loskutov.bco.asm.DecompiledMethod; import de.loskutov.bco.asm.DecompilerHelper; import de.loskutov.bco.asm.DecompilerOptions; import de.loskutov.bco.asm.LineRange; import de.loskutov.bco.editors.BytecodeClassFileEditor; import de.loskutov.bco.preferences.BCOConstants; import de.loskutov.bco.ui.EclipseUtils; import de.loskutov.bco.ui.JdtUtils; import de.loskutov.bco.ui.actions.DefaultToggleAction; /** * This view shows decompiled java bytecode * @author Andrei */ public class BytecodeOutlineView extends ViewPart { // 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 <code>VIEW_ORIENTATION_HORIZONTAL</code> * <code>VIEW_ORIENTATION_VERTICAL</code>, * or <code>VIEW_ORIENTATION_AUTOMATIC</code>. */ int orientation = VIEW_ORIENTATION_AUTOMATIC; /** * The current orientation; either <code>VIEW_ORIENTATION_HORIZONTAL</code> * <code>VIEW_ORIENTATION_VERTICAL</code>. */ 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<String, IAction> globalActions; protected List<String> 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; private static final String NLS_PREFIX = "BytecodeOutlineView."; // 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; private BytecodeClassFileEditor dummyEditorForHyperlinks; // ------------------------------------------------------------------------ protected void setJavaInput(IJavaElement javaInput) { this.javaInput = javaInput; inputChanged = true; } /** * The constructor. */ public BytecodeOutlineView() { super(); modes = new BitSet(); globalActions = new HashMap<String, IAction>(); selectionActions = new ArrayList<String>(); } // ------------------------------------------------------------------------ /** * 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 (int i = 0; i < toggleOrientationActions.length; ++i) { toggleOrientationActions[i].setEnabled(showAnalyzer); } } /** * Is this view monitoring workspace changes? * @return Returns the isActive. */ private boolean isActive() { return isActive; } /** * @param bufferIsDirty The bufferIsDirty to set. */ 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() { 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; } // ------------------------------------------------------------------------ /** * @see org.eclipse.ui.IViewPart#init(org.eclipse.ui.IViewSite) */ @Override public void init(IViewSite site) { super.setSite(site); if (editorListener == null) { editorListener = new EditorListener(this); getSite().getWorkbenchWindow().getPartService().addPartListener(editorListener); } } /** * This is a callback that will allow us to create the viewer and initialize it. * @param parent */ @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)); 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); // activateView(); } 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)) { 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(this, VIEW_ORIENTATION_VERTICAL), new ToggleOrientationAction(this, VIEW_ORIENTATION_HORIZONTAL), new ToggleOrientationAction(this, VIEW_ORIENTATION_AUTOMATIC) }; for (int i = 0; i < toggleOrientationActions.length; ++i) { mmanager.add(toggleOrientationActions[i]); } 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("#"); tc.setToolTipText("ASM instruction offset"); tc = new TableColumn(tableControl, SWT.LEFT); tc.setText(BytecodeOutlinePlugin.getResourceString(NLS_PREFIX + "lvt.header")); tc.setToolTipText("Local variables"); tc = new TableColumn(tableControl, SWT.LEFT); tc.setText(BytecodeOutlinePlugin.getResourceString(NLS_PREFIX + "stack.header")); tc.setToolTipText("Stack content *before* current instruction is executed"); 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("#"); new TableColumn(lvtTable, SWT.LEFT).setText("Var Type"); new TableColumn(lvtTable, SWT.LEFT).setText("Name"); stackTable = new Table(stackAndLvt, SWT.SINGLE | SWT.FULL_SELECTION); stackTable.setLinesVisible(false); stackTable.setHeaderVisible(true); new TableColumn(stackTable, SWT.LEFT).setText("#"); new TableColumn(stackTable, SWT.LEFT).setText("Stack Type"); stackAndLvt.setWeights(new int[] { 50, 50 }); verifyControl.setWeights(new int[] { 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 = "de.loskutov.bco.views.BytecodeOutlineView#ContextMenu"; //$NON-NLS-1$ contextMenuManager = new MenuManager("#ContextMenu", id); //$NON-NLS-1$ contextMenuManager.setRemoveAllWhenShown(true); contextMenuManager.addMenuListener(new IMenuListener() { @Override public void menuAboutToShow(IMenuManager m) { contextMenuAboutToShow(m); } }); 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); dummyEditorForHyperlinks = new BytecodeClassFileEditor() { @Override public IWorkbenchPartSite getSite() { return javaEditor == null ? null : javaEditor.getEditorSite(); } @Override public IEditorInput getEditorInput() { return javaEditor == null ? null : javaEditor.getEditorInput(); } @Override public IAction getAction(String actionID) { return javaEditor == null ? null : javaEditor.getAction(actionID); } }; JavaSourceViewerConfiguration configuration = new JavaConfiguration( JavaPlugin.getDefault().getJavaTextTools().getColorManager(), store, dummyEditorForHyperlinks, IJavaPartitions.JAVA_PARTITIONING); viewer.configure(configuration); viewer.setEditable(false); textViewer = viewer; textControl = textViewer.getTextWidget(); IDocument document = new Document(""); textViewer.setDocument(document); textSelectionListener = new ISelectionChangedListener() { @Override public void selectionChanged(SelectionChangedEvent event) { // Updates selection dependent actions like find/copy. Iterator<String> iterator = selectionActions.iterator(); while (iterator.hasNext()) { updateAction(iterator.next()); } } }; textListener = new ITextListener() { @Override public void textChanged(TextEvent 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(); } } }); } /** * @see org.eclipse.ui.IWorkbenchPart#dispose() */ @Override public void dispose() { deActivateView(); if (editorListener != null) { getSite().getWorkbenchWindow().getPartService().removePartListener(editorListener); getSite().getWorkbenchWindow().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(); } /** * Fill the context menu * @param menuManager menu */ 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)); } // ----------------------------------------------------------------------- /** * Passing the focus request to the viewer's control. */ @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(""); textViewer.setDocument(document); } if (tableControl != null && !tableControl.isDisposed()) { setVerifyTableItems(null); } /* * if(stackControl != null && !stackControl.isDisposed()){ * stackControl.setText(""); } if(lvtControl != null && !lvtControl.isDisposed()){ * lvtControl.setText(""); } */ 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(""); 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); // JavaSourceViewerConfiguration configuration = new JavaSourceViewerConfiguration( // JavaPlugin.getDefault().getJavaTextTools().getColorManager(), // JavaPlugin.getDefault().getCombinedPreferenceStore(), javaEditor, // IJavaPartitions.JAVA_PARTITIONING); // textViewer.unconfigure(); // textViewer.configure(configuration); 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<Integer> 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 (int i = 0; i < errors.size(); ++i) { int l = errors.get(i).intValue(); tableControl.getItem(l).setForeground(errorColor); } toggleVerifierAction.setEnabled(true); } /** * @param result */ private void updateStatus(DecompiledClass result, int bytecodeOffsetStart, int bytecodeOffsetEnd) { // clear error messages, if any statusLineManager.setErrorMessage(null); if (result != null) { currentStatusMessage = "Java:" + result.getJavaVersion() + " | class size:" + result.getClassSize(); ClassNode classNode = result.getClassNode(); if (classNode != null && classNode.name != null) { setContentDescription(classNode.name); } } else { currentStatusMessage = ""; setContentDescription(""); } String selectionInfo = ""; if (bytecodeOffsetStart >= 0) { selectionInfo = " | offset:" + bytecodeOffsetStart; if (bytecodeOffsetEnd >= 0) { selectionInfo += "-" + bytecodeOffsetEnd; } } if (hasAnalyzerError) { statusLineManager.setErrorMessage(currentStatusMessage + selectionInfo); } else { statusLineManager.setMessage(currentStatusMessage + selectionInfo); } } 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)); 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 */ 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 * @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 != 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 * @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; } InputStream is = JdtUtils.createInputStream(type); if (is == null) { return null; } DecompiledClass decompiledClass = null; int available = 0; try { ClassLoader cl = null; if (modes.get(BCOConstants.F_SHOW_ANALYZER)) { cl = JdtUtils.getClassLoader(type); } 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); } } available = is.available(); decompiledClass = DecompilerHelper.getDecompiledClass(is, new DecompilerOptions(fieldName, methodName, modes, cl)); } 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); } 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 + ". Error was caused by attempt to " + "load a class compiled with the Java version which is not " + "supported by the current JVM. ", e); } finally { try { is.close(); } catch (IOException e) { BytecodeOutlinePlugin.log(e, IStatus.WARNING); } } // 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")) { 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 Object getAdapter(Class adapter) { if (IFindReplaceTarget.class.equals(adapter)) { return textViewer.getFindReplaceTarget(); } if (Widget.class.equals(adapter)) { return textViewer.getTextWidget(); } if (TextViewer.class.equals(adapter)) { return 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(BytecodeOutlinePlugin.getResourceString(NLS_PREFIX + "select_all.label"), BytecodeOutlinePlugin.getResourceString(NLS_PREFIX + "select_all.tooltip"), BytecodeOutlinePlugin.getResourceString(NLS_PREFIX + "select_all.description")); setGlobalAction(actionBars, ActionFactory.SELECT_ALL.getId(), action); action = new TextViewerAction(textViewer, ITextOperationTarget.COPY); action.configureAction(BytecodeOutlinePlugin.getResourceString(NLS_PREFIX + "copy.label"), BytecodeOutlinePlugin.getResourceString(NLS_PREFIX + "copy.tooltip"), BytecodeOutlinePlugin.getResourceString(NLS_PREFIX + "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 = BytecodeOutlinePlugin.getDefault().getResourceBundle(); setGlobalAction(actionBars, ActionFactory.FIND.getId(), new FindReplaceAction(bundle, NLS_PREFIX + "find_replace.", this)); //$NON-NLS-1$ selectionActions.add(ActionFactory.COPY.getId()); selectionActions.add(ActionFactory.FIND.getId()); actionBars.updateActionBars(); } // orientation 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 (int i = 0; i < toggleOrientationActions.length; ++i) { toggleOrientationActions[i].setChecked(orientation == toggleOrientationActions[i].getOrientation()); } currentOrientation = orientation; // GridLayout layout= (GridLayout) fCounterComposite.getLayout(); // setCounterColumns(layout); 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 * @param value */ 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 (int i = 0; i < toggleOrientationActions.length; ++i) { toggleOrientationActions[i].setEnabled(showAnalyzer); } mmanager.markDirty(); mmanager.update(); } private class ToggleOrientationAction extends Action { private final int actionOrientation; public ToggleOrientationAction(BytecodeOutlineView v, int orientation) { super("", AS_RADIO_BUTTON); //$NON-NLS-1$ String symbolicName = BytecodeOutlinePlugin.getDefault().getBundle().getSymbolicName(); if (orientation == VIEW_ORIENTATION_HORIZONTAL) { setText(BytecodeOutlinePlugin.getResourceString(NLS_PREFIX + "toggle.horizontal.label")); //$NON-NLS-1$ setImageDescriptor( AbstractUIPlugin.imageDescriptorFromPlugin(symbolicName, "icons/th_horizontal.gif")); //$NON-NLS-1$ } else if (orientation == VIEW_ORIENTATION_VERTICAL) { setText(BytecodeOutlinePlugin.getResourceString(NLS_PREFIX + "toggle.vertical.label")); //$NON-NLS-1$ setImageDescriptor( AbstractUIPlugin.imageDescriptorFromPlugin(symbolicName, "icons/th_vertical.gif")); //$NON-NLS-1$ } else if (orientation == VIEW_ORIENTATION_AUTOMATIC) { setText(BytecodeOutlinePlugin.getResourceString(NLS_PREFIX + "toggle.automatic.label")); //$NON-NLS-1$ setImageDescriptor( AbstractUIPlugin.imageDescriptorFromPlugin(symbolicName, "icons/th_automatic.gif")); //$NON-NLS-1$ } actionOrientation = orientation; // WorkbenchHelp.setHelp(this, // IJUnitHelpContextIds.RESULTS_VIEW_TOGGLE_ORIENTATION_ACTION); } 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("$")) { 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 || dummyEditorForHyperlinks == null || javaInput == null) { return null; } IAction openAction = dummyEditorForHyperlinks.getAction("OpenEditor"); //$NON-NLS-1$ if (!(openAction instanceof SelectionDispatchAction)) { return null; } int offset = region.getOffset(); IDocument document = textViewer1.getDocument(); IRegion wordRegion = JavaWordFinder.findWord(document, offset); List<IHyperlink> links = new ArrayList<IHyperlink>(); 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 (int i = 0; i < elements.length; i++) { if (elements[i] == null) { continue; } addHyperlinks2(links, wordRegion, (SelectionDispatchAction) openAction, elements[i], elements.length > 1, dummyEditorForHyperlinks); } 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! * <p> * 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 <code>true</code> if the hyperlink text should show a qualified name for * element * @param editor the active Java editor * * @since 3.7.1 */ protected void addHyperlinks2(List<IHyperlink> hyperlinksCollector, IRegion wordRegion, SelectionDispatchAction openAction, IJavaElement element, boolean qualify, JavaEditor editor) { 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) { JavadocHover javadocHover = new JavadocHoverExtension(); return javadocHover; } @Override public IQuickAssistAssistant getQuickAssistAssistant(ISourceViewer sourceViewer) { return null; } } private final class JavadocHoverExtension extends JavadocHover { private final Set<String> OPCODES = new HashSet<String>(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) { JavadocBrowserInformationControlInput input = new JavadocBrowserInformationControlInput(null, null, sb.toString(), 0); return input; } return null; } } }