blob: 96aaf3ae3cab83ec0cfa357a57d9ab02ff95a229 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2000, 2004 IBM Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Common Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/cpl-v10.html
*
* Contributors:
* IBM Corporation - initial API and implementation
*******************************************************************************/
package org.eclipse.jdt.astview.views;
import org.eclipse.jdt.astview.ASTViewImages;
import org.eclipse.jdt.astview.ASTViewPlugin;
import org.eclipse.jdt.astview.EditorUtility;
import org.eclipse.jdt.astview.NodeFinder;
import org.eclipse.core.filebuffers.FileBuffers;
import org.eclipse.core.filebuffers.IFileBuffer;
import org.eclipse.core.filebuffers.IFileBufferListener;
import org.eclipse.core.filebuffers.ITextFileBuffer;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.swt.SWT;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Menu;
import org.eclipse.jface.action.Action;
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.dialogs.ErrorDialog;
import org.eclipse.jface.text.DocumentEvent;
import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.text.IDocumentListener;
import org.eclipse.jface.text.ITextSelection;
import org.eclipse.jface.viewers.AbstractTreeViewer;
import org.eclipse.jface.viewers.DoubleClickEvent;
import org.eclipse.jface.viewers.IDoubleClickListener;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.ISelectionChangedListener;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.viewers.SelectionChangedEvent;
import org.eclipse.jface.viewers.StructuredSelection;
import org.eclipse.jface.viewers.TreeViewer;
import org.eclipse.ui.IActionBars;
import org.eclipse.ui.IEditorPart;
import org.eclipse.ui.ISelectionListener;
import org.eclipse.ui.ISelectionService;
import org.eclipse.ui.IViewSite;
import org.eclipse.ui.IWorkbenchActionConstants;
import org.eclipse.ui.IWorkbenchPart;
import org.eclipse.ui.PartInitException;
import org.eclipse.ui.part.DrillDownAdapter;
import org.eclipse.ui.part.ViewPart;
import org.eclipse.ui.texteditor.ITextEditor;
import org.eclipse.jdt.core.IBuffer;
import org.eclipse.jdt.core.IClassFile;
import org.eclipse.jdt.core.ICompilationUnit;
import org.eclipse.jdt.core.IOpenable;
import org.eclipse.jdt.core.dom.AST;
import org.eclipse.jdt.core.dom.ASTNode;
import org.eclipse.jdt.core.dom.ASTParser;
import org.eclipse.jdt.core.dom.CompilationUnit;
import org.eclipse.jdt.core.dom.IBinding;
public class ASTView extends ViewPart {
private class ASTLevelToggle extends Action {
private int fLevel;
public ASTLevelToggle(String label, int level) {
super(label, AS_RADIO_BUTTON);
fLevel= level;
if (level == getCurrentASTLevel()) {
setChecked(true);
}
}
public int getLevel() {
return fLevel;
}
public void run() {
performLevelToggle(fLevel);
}
}
private class SuperListener implements ISelectionListener, IFileBufferListener, IDocumentListener, ISelectionChangedListener, IDoubleClickListener {
public void selectionChanged(IWorkbenchPart part, ISelection selection) {
handleEditorPostSelectionChanged(part, selection);
}
/* (non-Javadoc)
* @see org.eclipse.core.filebuffers.IFileBufferListener#bufferCreated(org.eclipse.core.filebuffers.IFileBuffer)
*/
public void bufferCreated(IFileBuffer buffer) {
}
/* (non-Javadoc)
* @see org.eclipse.core.filebuffers.IFileBufferListener#bufferDisposed(org.eclipse.core.filebuffers.IFileBuffer)
*/
public void bufferDisposed(IFileBuffer buffer) {
if (buffer instanceof ITextFileBuffer) {
handleDocumentDisposed(((ITextFileBuffer) buffer).getDocument());
}
}
/* (non-Javadoc)
* @see org.eclipse.core.filebuffers.IFileBufferListener#bufferContentAboutToBeReplaced(org.eclipse.core.filebuffers.IFileBuffer)
*/
public void bufferContentAboutToBeReplaced(IFileBuffer buffer) {
}
/* (non-Javadoc)
* @see org.eclipse.core.filebuffers.IFileBufferListener#bufferContentReplaced(org.eclipse.core.filebuffers.IFileBuffer)
*/
public void bufferContentReplaced(IFileBuffer buffer) {
}
/* (non-Javadoc)
* @see org.eclipse.core.filebuffers.IFileBufferListener#stateChanging(org.eclipse.core.filebuffers.IFileBuffer)
*/
public void stateChanging(IFileBuffer buffer) {
}
/* (non-Javadoc)
* @see org.eclipse.core.filebuffers.IFileBufferListener#dirtyStateChanged(org.eclipse.core.filebuffers.IFileBuffer, boolean)
*/
public void dirtyStateChanged(IFileBuffer buffer, boolean isDirty) {
}
/* (non-Javadoc)
* @see org.eclipse.core.filebuffers.IFileBufferListener#stateValidationChanged(org.eclipse.core.filebuffers.IFileBuffer, boolean)
*/
public void stateValidationChanged(IFileBuffer buffer, boolean isStateValidated) {
}
/* (non-Javadoc)
* @see org.eclipse.core.filebuffers.IFileBufferListener#underlyingFileMoved(org.eclipse.core.filebuffers.IFileBuffer, org.eclipse.core.runtime.IPath)
*/
public void underlyingFileMoved(IFileBuffer buffer, IPath path) {
}
/* (non-Javadoc)
* @see org.eclipse.core.filebuffers.IFileBufferListener#underlyingFileDeleted(org.eclipse.core.filebuffers.IFileBuffer)
*/
public void underlyingFileDeleted(IFileBuffer buffer) {
}
/* (non-Javadoc)
* @see org.eclipse.core.filebuffers.IFileBufferListener#stateChangeFailed(org.eclipse.core.filebuffers.IFileBuffer)
*/
public void stateChangeFailed(IFileBuffer buffer) {
}
/* (non-Javadoc)
* @see org.eclipse.jface.text.IDocumentListener#documentAboutToBeChanged(org.eclipse.jface.text.DocumentEvent)
*/
public void documentAboutToBeChanged(DocumentEvent event) {
}
/* (non-Javadoc)
* @see org.eclipse.jface.text.IDocumentListener#documentChanged(org.eclipse.jface.text.DocumentEvent)
*/
public void documentChanged(DocumentEvent event) {
handleDocumentChanged(event.getDocument());
}
/* (non-Javadoc)
* @see org.eclipse.jface.viewers.ISelectionChangedListener#selectionChanged(org.eclipse.jface.viewers.SelectionChangedEvent)
*/
public void selectionChanged(SelectionChangedEvent event) {
handleSelectionChanged(event.getSelection());
}
/* (non-Javadoc)
* @see org.eclipse.jface.viewers.IDoubleClickListener#doubleClick(org.eclipse.jface.viewers.DoubleClickEvent)
*/
public void doubleClick(DoubleClickEvent event) {
handleDoubleClick(event);
}
}
private TreeViewer fViewer;
private DrillDownAdapter fDrillDownAdapter;
private Action fFocusAction;
private Action fRefreshAction;
private Action fCollapseAction;
private Action fExpandAction;
private Action fDoubleClickAction;
private Action fLinkWithEditor;
private ASTLevelToggle[] fASTVersionToggleActions;
private int fCurrentASTLevel;
private ITextEditor fEditor;
private IOpenable fOpenable;
private CompilationUnit fRoot;
private IDocument fCurrentDocument;
private boolean fDoLinkWithEditor;
private Object fPreviousDouble;
private SuperListener fSuperListener;
public ASTView() {
fSuperListener= null;
fDoLinkWithEditor= false;
fCurrentASTLevel= AST.LEVEL_2_0;
}
/*(non-Javadoc)
* @see org.eclipse.ui.IViewPart#init(org.eclipse.ui.IViewSite)
*/
public void init(IViewSite site) throws PartInitException {
super.setSite(site);
if (fSuperListener == null) {
fSuperListener= new SuperListener();
ISelectionService service= site.getWorkbenchWindow().getSelectionService();
service.addPostSelectionListener(fSuperListener);
FileBuffers.getTextFileBufferManager().addFileBufferListener(fSuperListener);
}
}
public int getCurrentASTLevel() {
return fCurrentASTLevel;
}
public void setInput(ITextEditor editor) throws CoreException {
if (fEditor != null) {
uninstallModificationListener();
}
fEditor= null;
fRoot= null;
if (editor != null) {
IOpenable openable= EditorUtility.getJavaInput(editor);
if (openable == null) {
throw new CoreException(getErrorStatus("Editor not showing a CU or classfile", null)); //$NON-NLS-1$
}
fOpenable= openable;
ISelection selection= editor.getSelectionProvider().getSelection();
if (selection instanceof ITextSelection) {
ITextSelection textSelection= (ITextSelection) selection;
fRoot= internalSetInput(openable, textSelection.getOffset(), textSelection.getLength());
fEditor= editor;
}
installModificationListener();
}
}
private CompilationUnit internalSetInput(IOpenable input, int offset, int length) throws CoreException {
IBuffer buffer= input.getBuffer();
if (buffer == null) {
throw new CoreException(getErrorStatus("Input has no buffer", null)); //$NON-NLS-1$
}
ASTParser parser= ASTParser.newParser(getCurrentASTLevel());
parser.setResolveBindings(true);
if (input instanceof ICompilationUnit) {
parser.setSource((ICompilationUnit) input);
} else {
parser.setSource((IClassFile) input);
}
try {
CompilationUnit root= (CompilationUnit) parser.createAST(null);
if (root == null) {
throw new CoreException(getErrorStatus("Could not create AST", null)); //$NON-NLS-1$
}
fViewer.setInput(root);
setASTUptoDate(true);
ASTNode node= NodeFinder.perform(root, offset, length);
if (node != null) {
fViewer.getTree().setRedraw(false);
fViewer.setSelection(new StructuredSelection(node), true);
fViewer.getTree().setRedraw(true);
}
return root;
} catch (RuntimeException e) {
throw new CoreException(getErrorStatus("Could not create AST:\n" + e.getMessage(), e)); //$NON-NLS-1$
}
}
/* (non-Javadoc)
* @see org.eclipse.ui.IWorkbenchPart#dispose()
*/
public void dispose() {
if (fSuperListener != null) {
if (fEditor != null) {
uninstallModificationListener();
}
ISelectionService service= getSite().getWorkbenchWindow().getSelectionService();
service.removePostSelectionListener(fSuperListener);
FileBuffers.getTextFileBufferManager().removeFileBufferListener(fSuperListener);
fSuperListener= null;
}
super.dispose();
}
private IStatus getErrorStatus(String message, Throwable th) {
return new Status(IStatus.ERROR, ASTViewPlugin.getDefault().getDescriptor().getUniqueIdentifier(), IStatus.ERROR, message, th);
}
/*
* (non-Javadoc)
* @see org.eclipse.ui.IWorkbenchPart#createPartControl(org.eclipse.swt.widgets.Composite)
*/
public void createPartControl(Composite parent) {
fViewer = new TreeViewer(parent, SWT.MULTI | SWT.H_SCROLL | SWT.V_SCROLL);
fDrillDownAdapter = new DrillDownAdapter(fViewer);
fViewer.setContentProvider(new ASTViewContentProvider());
fViewer.setLabelProvider(new ASTViewLabelProvider());
fViewer.addSelectionChangedListener(fSuperListener);
fViewer.addDoubleClickListener(fSuperListener);
makeActions();
hookContextMenu();
contributeToActionBars();
performRefresh();
setASTUptoDate(fOpenable != null);
}
private void hookContextMenu() {
MenuManager menuMgr = new MenuManager("#PopupMenu"); //$NON-NLS-1$
menuMgr.setRemoveAllWhenShown(true);
menuMgr.addMenuListener(new IMenuListener() {
public void menuAboutToShow(IMenuManager manager) {
ASTView.this.fillContextMenu(manager);
}
});
Menu menu = menuMgr.createContextMenu(fViewer.getControl());
fViewer.getControl().setMenu(menu);
getSite().registerContextMenu(menuMgr, fViewer);
}
private void contributeToActionBars() {
IActionBars bars = getViewSite().getActionBars();
fillLocalPullDown(bars.getMenuManager());
fillLocalToolBar(bars.getToolBarManager());
}
private void fillLocalPullDown(IMenuManager manager) {
manager.add(fLinkWithEditor);
manager.add(new Separator());
for (int i= 0; i < fASTVersionToggleActions.length; i++) {
manager.add(fASTVersionToggleActions[i]);
}
}
private void fillContextMenu(IMenuManager manager) {
manager.add(fFocusAction);
manager.add(fRefreshAction);
manager.add(fCollapseAction);
manager.add(fExpandAction);
manager.add(new Separator());
fDrillDownAdapter.addNavigationActions(manager);
// Other plug-ins can contribute there actions here
manager.add(new Separator(IWorkbenchActionConstants.MB_ADDITIONS));
}
private void fillLocalToolBar(IToolBarManager manager) {
manager.add(fFocusAction);
manager.add(fRefreshAction);
manager.add(new Separator());
fDrillDownAdapter.addNavigationActions(manager);
manager.add(new Separator());
manager.add(fExpandAction);
manager.add(fCollapseAction);
manager.add(fLinkWithEditor);
}
private void setASTUptoDate(boolean isuptoDate) {
fRefreshAction.setEnabled(!isuptoDate && fOpenable != null);
}
private void makeActions() {
fRefreshAction = new Action() {
public void run() {
performRefresh();
}
};
fRefreshAction.setText("Refresh AST"); //$NON-NLS-1$
fRefreshAction.setToolTipText("Refresh AST"); //$NON-NLS-1$
fRefreshAction.setEnabled(false);
ASTViewImages.setImageDescriptors(fRefreshAction, ASTViewImages.REFRESH);
fFocusAction = new Action() {
public void run() {
performSetFocus();
}
};
fFocusAction.setText("Show AST of active editor"); //$NON-NLS-1$
fFocusAction.setToolTipText("Show AST of active editor"); //$NON-NLS-1$
ASTViewImages.setImageDescriptors(fFocusAction, ASTViewImages.SETFOCUS);
fCollapseAction = new Action() {
public void run() {
performCollapse();
}
};
fCollapseAction.setText("Collapse"); //$NON-NLS-1$
fCollapseAction.setToolTipText("Collapse Selected Node"); //$NON-NLS-1$
fCollapseAction.setEnabled(false);
ASTViewImages.setImageDescriptors(fCollapseAction, ASTViewImages.COLLAPSE);
fExpandAction = new Action() {
public void run() {
performExpand();
}
};
fExpandAction.setText("Expand"); //$NON-NLS-1$
fExpandAction.setToolTipText("Expand Selected Node"); //$NON-NLS-1$
fExpandAction.setEnabled(false);
ASTViewImages.setImageDescriptors(fExpandAction, ASTViewImages.EXPAND);
fDoubleClickAction = new Action() {
public void run() {
performDoubleClick();
}
};
fLinkWithEditor = new Action() {
public void run() {
performLinkWithEditor();
}
};
fLinkWithEditor.setChecked(false);
fLinkWithEditor.setText("Link with editor"); //$NON-NLS-1$
fLinkWithEditor.setToolTipText("Link With Editor"); //$NON-NLS-1$
ASTViewImages.setImageDescriptors(fLinkWithEditor, ASTViewImages.LINK_WITH_EDITOR);
fASTVersionToggleActions= new ASTLevelToggle[] {
new ASTLevelToggle("AST Level 2.0", AST.LEVEL_2_0), //$NON-NLS-1$
new ASTLevelToggle("AST Level 3.0", AST.LEVEL_3_0) //$NON-NLS-1$
};
}
private void refreshAST() throws CoreException {
ASTNode node= getASTNodeNearSelection((IStructuredSelection) fViewer.getSelection());
int offset= 0;
int length= 0;
if (node != null) {
offset= node.getStartPosition();
length= node.getLength();
}
internalSetInput(fOpenable, offset, length);
}
protected void performLevelToggle(int level) {
int oldLevel= fCurrentASTLevel;
fCurrentASTLevel= level;
if (fOpenable == null) {
return;
}
try {
refreshAST();
} catch (CoreException e) {
ErrorDialog.openError(getSite().getShell(), "AST View", "Could not set AST to new level.", e.getStatus()); //$NON-NLS-1$ //$NON-NLS-2$
// change to previous
for (int i= 0; i < fASTVersionToggleActions.length; i++) {
ASTLevelToggle curr= fASTVersionToggleActions[i];
curr.setChecked(curr.getLevel() == oldLevel);
}
fCurrentASTLevel= oldLevel;
}
}
private ASTNode getASTNodeNearSelection(IStructuredSelection selection) {
Object elem= selection.getFirstElement();
if (elem instanceof NodeProperty) {
Object node= ((NodeProperty) elem).getNode();
if (node instanceof ASTNode) {
return (ASTNode) node;
}
return ((NodeProperty) elem).getParent();
} else if (elem instanceof ASTNode) {
return (ASTNode) elem;
} else if (elem instanceof BindingProperty) {
return ((BindingProperty) elem).getParent();
}
return null;
}
private void installModificationListener() {
fCurrentDocument= fEditor.getDocumentProvider().getDocument(fEditor.getEditorInput());
fCurrentDocument.addDocumentListener(fSuperListener);
}
private void uninstallModificationListener() {
if (fCurrentDocument != null) {
fCurrentDocument.removeDocumentListener(fSuperListener);
fCurrentDocument= null;
}
}
protected void handleDocumentDisposed(IDocument document) {
uninstallModificationListener();
}
protected void handleDocumentChanged(IDocument document) {
setASTUptoDate(false);
}
protected void handleSelectionChanged(ISelection selection) {
fExpandAction.setEnabled(!selection.isEmpty());
fCollapseAction.setEnabled(!selection.isEmpty());
}
protected void handleEditorPostSelectionChanged(IWorkbenchPart part, ISelection selection) {
if (!fDoLinkWithEditor || fRoot == null || !(selection instanceof ITextSelection)) {
return;
}
if (part != fEditor && part instanceof ITextEditor && (EditorUtility.getJavaInput((ITextEditor) part) != null)) {
try {
setInput((ITextEditor) part);
} catch (CoreException e) {
// ignore
}
return;
}
ITextSelection textSelection= (ITextSelection) selection;
int offset= textSelection.getOffset();
int length= textSelection.getLength();
NodeFinder finder= new NodeFinder(offset, length);
fRoot.accept(finder);
ASTNode covering= finder.getCoveringNode();
fViewer.reveal(covering);
fViewer.setSelection(new StructuredSelection(covering));
}
public void handleDoubleClick(DoubleClickEvent event) {
fDoubleClickAction.run();
}
protected void performLinkWithEditor() {
fDoLinkWithEditor= fLinkWithEditor.isChecked();
}
protected void performCollapse() {
IStructuredSelection selection= (IStructuredSelection) fViewer.getSelection();
if (selection.isEmpty()) {
fViewer.collapseAll();
} else {
Object[] selected= selection.toArray();
fViewer.getTree().setRedraw(false);
for (int i= 0; i < selected.length; i++) {
fViewer.collapseToLevel(selected[i], AbstractTreeViewer.ALL_LEVELS);
}
fViewer.getTree().setRedraw(true);
}
}
protected void performExpand() {
IStructuredSelection selection= (IStructuredSelection) fViewer.getSelection();
if (selection.isEmpty()) {
fViewer.expandAll();
} else {
Object[] selected= selection.toArray();
fViewer.getTree().setRedraw(false);
for (int i= 0; i < selected.length; i++) {
fViewer.expandToLevel(selected[i], AbstractTreeViewer.ALL_LEVELS);
}
fViewer.getTree().setRedraw(true);
}
}
protected void performSetFocus() {
IEditorPart part= EditorUtility.getActiveEditor();
if (part instanceof ITextEditor) {
try {
setInput((ITextEditor) part);
} catch (CoreException e) {
ErrorDialog.openError(getSite().getShell(), "AST View", "Could not set AST view input ", e.getStatus()); //$NON-NLS-1$ //$NON-NLS-2$
}
}
}
protected void performRefresh() {
if (fOpenable != null) {
try {
refreshAST();
} catch (CoreException e) {
ErrorDialog.openError(getSite().getShell(), "AST View", "Could not set AST view input ", e.getStatus()); //$NON-NLS-1$ //$NON-NLS-2$
}
}
}
protected void performDoubleClick() {
ISelection selection = fViewer.getSelection();
Object obj = ((IStructuredSelection) selection).getFirstElement();
boolean isTrippleClick= (obj == fPreviousDouble);
fPreviousDouble= isTrippleClick ? null : obj;
ASTNode node= null;
if (obj instanceof ASTNode) {
node= (ASTNode) obj;
} else if (obj instanceof NodeProperty) {
Object val= ((NodeProperty) obj).getNode();
if (val instanceof ASTNode) {
node= (ASTNode) val;
}
} else if (obj instanceof BindingProperty) {
IBinding binding= ((BindingProperty) obj).getBinding();
ASTNode declaring= fRoot.findDeclaringNode(binding);
if (declaring != null) {
fViewer.reveal(declaring);
fViewer.setSelection(new StructuredSelection(declaring));
}
return;
}
if (node != null) {
int offset= isTrippleClick ? fRoot.getExtendedStartPosition(node) : node.getStartPosition();
int length= isTrippleClick ? fRoot.getExtendedLength(node) : node.getLength();
EditorUtility.selectInEditor(fEditor, offset, length);
}
}
public void setFocus() {
fViewer.getControl().setFocus();
}
}