blob: 18a65335664377d00a19ce2daf86b718ec843505 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2000, 2015 IBM Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* IBM Corporation - initial API and implementation
*******************************************************************************/
package org.eclipse.jdt.internal.ui.javaeditor;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Vector;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.BusyIndicator;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Item;
import org.eclipse.swt.widgets.Menu;
import org.eclipse.swt.widgets.Tree;
import org.eclipse.swt.widgets.Widget;
import org.eclipse.core.runtime.Assert;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IAdaptable;
import org.eclipse.core.runtime.ListenerList;
import org.eclipse.core.resources.IResource;
import org.eclipse.text.edits.MalformedTreeException;
import org.eclipse.text.edits.TextEdit;
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.IStatusLineManager;
import org.eclipse.jface.action.IToolBarManager;
import org.eclipse.jface.action.MenuManager;
import org.eclipse.jface.action.Separator;
import org.eclipse.jface.commands.ActionHandler;
import org.eclipse.jface.preference.IPreferenceStore;
import org.eclipse.jface.util.IPropertyChangeListener;
import org.eclipse.jface.util.PropertyChangeEvent;
import org.eclipse.jface.viewers.IBaseLabelProvider;
import org.eclipse.jface.viewers.IPostSelectionProvider;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.ISelectionChangedListener;
import org.eclipse.jface.viewers.ISelectionProvider;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.viewers.ITreeContentProvider;
import org.eclipse.jface.viewers.LabelProviderChangedEvent;
import org.eclipse.jface.viewers.StructuredSelection;
import org.eclipse.jface.viewers.TreeViewer;
import org.eclipse.jface.viewers.Viewer;
import org.eclipse.jface.text.BadLocationException;
import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.text.IRegion;
import org.eclipse.jface.text.ITextSelection;
import org.eclipse.jface.text.Region;
import org.eclipse.jface.text.TextUtilities;
import org.eclipse.ui.IActionBars;
import org.eclipse.ui.IWorkbenchCommandConstants;
import org.eclipse.ui.OpenAndLinkWithEditorHelper;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.actions.ActionContext;
import org.eclipse.ui.actions.ActionGroup;
import org.eclipse.ui.handlers.CollapseAllHandler;
import org.eclipse.ui.handlers.IHandlerService;
import org.eclipse.ui.model.IWorkbenchAdapter;
import org.eclipse.ui.model.WorkbenchAdapter;
import org.eclipse.ui.part.IPageSite;
import org.eclipse.ui.part.IShowInSource;
import org.eclipse.ui.part.IShowInTarget;
import org.eclipse.ui.part.IShowInTargetList;
import org.eclipse.ui.part.Page;
import org.eclipse.ui.part.ShowInContext;
import org.eclipse.ui.views.contentoutline.IContentOutlinePage;
import org.eclipse.ui.texteditor.ITextEditorActionConstants;
import org.eclipse.ui.texteditor.ITextEditorActionDefinitionIds;
import org.eclipse.ui.texteditor.IUpdate;
import org.eclipse.jdt.core.ElementChangedEvent;
import org.eclipse.jdt.core.IClassFile;
import org.eclipse.jdt.core.ICompilationUnit;
import org.eclipse.jdt.core.IElementChangedListener;
import org.eclipse.jdt.core.IJavaElement;
import org.eclipse.jdt.core.IJavaElementDelta;
import org.eclipse.jdt.core.IParent;
import org.eclipse.jdt.core.ISourceRange;
import org.eclipse.jdt.core.ISourceReference;
import org.eclipse.jdt.core.IType;
import org.eclipse.jdt.core.ITypeRoot;
import org.eclipse.jdt.core.JavaCore;
import org.eclipse.jdt.core.JavaModelException;
import org.eclipse.jdt.core.formatter.CodeFormatter;
import org.eclipse.jdt.internal.corext.util.CodeFormatterUtil;
import org.eclipse.jdt.internal.corext.util.JavaModelUtil;
import org.eclipse.jdt.ui.JavaElementComparator;
import org.eclipse.jdt.ui.JavaElementLabels;
import org.eclipse.jdt.ui.JavaUI;
import org.eclipse.jdt.ui.PreferenceConstants;
import org.eclipse.jdt.ui.ProblemsLabelDecorator.ProblemsLabelChangedEvent;
import org.eclipse.jdt.ui.actions.CCPActionGroup;
import org.eclipse.jdt.ui.actions.CustomFiltersActionGroup;
import org.eclipse.jdt.ui.actions.GenerateActionGroup;
import org.eclipse.jdt.ui.actions.IJavaEditorActionDefinitionIds;
import org.eclipse.jdt.ui.actions.JavaSearchActionGroup;
import org.eclipse.jdt.ui.actions.JdtActionConstants;
import org.eclipse.jdt.ui.actions.MemberFilterActionGroup;
import org.eclipse.jdt.ui.actions.OpenViewActionGroup;
import org.eclipse.jdt.ui.actions.RefactorActionGroup;
import org.eclipse.jdt.ui.actions.SelectionDispatchAction;
import org.eclipse.jdt.internal.ui.IJavaHelpContextIds;
import org.eclipse.jdt.internal.ui.JavaPlugin;
import org.eclipse.jdt.internal.ui.JavaPluginImages;
import org.eclipse.jdt.internal.ui.actions.AbstractToggleLinkingAction;
import org.eclipse.jdt.internal.ui.actions.CategoryFilterActionGroup;
import org.eclipse.jdt.internal.ui.actions.CollapseAllAction;
import org.eclipse.jdt.internal.ui.actions.CompositeActionGroup;
import org.eclipse.jdt.internal.ui.dnd.JdtViewerDragSupport;
import org.eclipse.jdt.internal.ui.dnd.JdtViewerDropSupport;
import org.eclipse.jdt.internal.ui.preferences.MembersOrderPreferenceCache;
import org.eclipse.jdt.internal.ui.util.ElementValidator;
import org.eclipse.jdt.internal.ui.viewsupport.AppearanceAwareLabelProvider;
import org.eclipse.jdt.internal.ui.viewsupport.DecoratingJavaLabelProvider;
import org.eclipse.jdt.internal.ui.viewsupport.SourcePositionComparator;
import org.eclipse.jdt.internal.ui.viewsupport.StatusBarUpdater;
/**
* The content outline page of the Java editor. The viewer implements a proprietary
* update mechanism based on Java model deltas. It does not react on domain changes.
* It is specified to show the content of ICompilationUnits and IClassFiles.
* Publishes its context menu under <code>JavaPlugin.getDefault().getPluginId() + ".outline"</code>.
*/
public class JavaOutlinePage extends Page implements IContentOutlinePage, IAdaptable , IPostSelectionProvider {
static Object[] NO_CHILDREN= new Object[0];
/**
* The element change listener of the java outline viewer.
* @see IElementChangedListener
*/
protected class ElementChangedListener implements IElementChangedListener {
public void elementChanged(final ElementChangedEvent e) {
if (getControl() == null)
return;
Display d= getControl().getDisplay();
if (d != null) {
d.asyncExec(new Runnable() {
public void run() {
ICompilationUnit cu= (ICompilationUnit) fInput;
IJavaElement base= cu;
if (fTopLevelTypeOnly) {
base= cu.findPrimaryType();
if (base == null) {
if (fOutlineViewer != null)
fOutlineViewer.refresh(true);
return;
}
}
IJavaElementDelta delta= findElement(base, e.getDelta());
if (delta != null && fOutlineViewer != null) {
fOutlineViewer.reconcile(delta);
}
}
});
}
}
private boolean isPossibleStructuralChange(IJavaElementDelta cuDelta) {
if (cuDelta.getKind() != IJavaElementDelta.CHANGED) {
return true; // add or remove
}
int flags= cuDelta.getFlags();
if ((flags & IJavaElementDelta.F_CHILDREN) != 0) {
return true;
}
return (flags & (IJavaElementDelta.F_CONTENT | IJavaElementDelta.F_FINE_GRAINED)) == IJavaElementDelta.F_CONTENT;
}
protected IJavaElementDelta findElement(IJavaElement unit, IJavaElementDelta delta) {
if (delta == null || unit == null)
return null;
IJavaElement element= delta.getElement();
if (unit.equals(element)) {
if (isPossibleStructuralChange(delta)) {
return delta;
}
return null;
}
if (element.getElementType() > IJavaElement.CLASS_FILE)
return null;
IJavaElementDelta[] children= delta.getAffectedChildren();
if (children == null || children.length == 0)
return null;
for (int i= 0; i < children.length; i++) {
IJavaElementDelta d= findElement(unit, children[i]);
if (d != null)
return d;
}
return null;
}
}
static class NoClassElement extends WorkbenchAdapter implements IAdaptable {
/*
* @see java.lang.Object#toString()
*/
@Override
public String toString() {
return JavaEditorMessages.JavaOutlinePage_error_NoTopLevelType;
}
@SuppressWarnings("unchecked")
public <T> T getAdapter(Class<T> clas) {
if (clas == IWorkbenchAdapter.class)
return (T) this;
return null;
}
}
/**
* Content provider for the children of an ICompilationUnit or
* an IClassFile
* @see ITreeContentProvider
*/
protected class ChildrenProvider implements ITreeContentProvider {
private Object[] NO_CLASS= new Object[] {new NoClassElement()};
private ElementChangedListener fListener;
protected boolean matches(IJavaElement element) {
if (element.getElementType() == IJavaElement.METHOD) {
String name= element.getElementName();
return (name != null && name.indexOf('<') >= 0);
}
return false;
}
protected IJavaElement[] filter(IJavaElement[] children) {
boolean initializers= false;
for (int i= 0; i < children.length; i++) {
if (matches(children[i])) {
initializers= true;
break;
}
}
if (!initializers)
return children;
Vector<IJavaElement> v= new Vector<IJavaElement>();
for (int i= 0; i < children.length; i++) {
if (matches(children[i]))
continue;
v.addElement(children[i]);
}
IJavaElement[] result= new IJavaElement[v.size()];
v.copyInto(result);
return result;
}
public Object[] getChildren(Object parent) {
if (parent instanceof IParent) {
IParent c= (IParent) parent;
try {
return filter(c.getChildren());
} catch (JavaModelException x) {
// https://bugs.eclipse.org/bugs/show_bug.cgi?id=38341
// don't log NotExist exceptions as this is a valid case
// since we might have been posted and the element
// removed in the meantime.
if (JavaPlugin.isDebug() || !x.isDoesNotExist())
JavaPlugin.log(x);
}
}
return NO_CHILDREN;
}
public Object[] getElements(Object parent) {
if (fTopLevelTypeOnly) {
if (parent instanceof ITypeRoot) {
try {
IType type= ((ITypeRoot) parent).findPrimaryType();
return type != null ? type.getChildren() : NO_CLASS;
} catch (JavaModelException e) {
JavaPlugin.log(e);
}
}
}
return getChildren(parent);
}
public Object getParent(Object child) {
if (child instanceof IJavaElement) {
IJavaElement e= (IJavaElement) child;
return e.getParent();
}
return null;
}
public boolean hasChildren(Object parent) {
if (parent instanceof IParent) {
IParent c= (IParent) parent;
try {
IJavaElement[] children= filter(c.getChildren());
return (children != null && children.length > 0);
} catch (JavaModelException x) {
// https://bugs.eclipse.org/bugs/show_bug.cgi?id=38341
// don't log NotExist exceptions as this is a valid case
// since we might have been posted and the element
// removed in the meantime.
if (JavaPlugin.isDebug() || !x.isDoesNotExist())
JavaPlugin.log(x);
}
}
return false;
}
public void dispose() {
if (fListener != null) {
JavaCore.removeElementChangedListener(fListener);
fListener= null;
}
}
/*
* @see IContentProvider#inputChanged(Viewer, Object, Object)
*/
public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
boolean isCU= (newInput instanceof ICompilationUnit);
if (isCU && fListener == null) {
fListener= new ElementChangedListener();
JavaCore.addElementChangedListener(fListener);
} else if (!isCU && fListener != null) {
JavaCore.removeElementChangedListener(fListener);
fListener= null;
}
}
}
/**
* The tree viewer used for displaying the outline.
*
* @see TreeViewer
*/
protected class JavaOutlineViewer extends TreeViewer {
public JavaOutlineViewer(Tree tree) {
super(tree);
setAutoExpandLevel(ALL_LEVELS);
setUseHashlookup(true);
}
/**
* Investigates the given element change event and if affected
* incrementally updates the Java outline.
*
* @param delta the Java element delta used to reconcile the Java outline
*/
public void reconcile(IJavaElementDelta delta) {
refresh(true);
}
/*
* @see TreeViewer#internalExpandToLevel
*/
@Override
protected void internalExpandToLevel(Widget node, int level) {
if (node instanceof Item) {
Item i= (Item) node;
if (i.getData() instanceof IJavaElement) {
IJavaElement je= (IJavaElement) i.getData();
if (je.getElementType() == IJavaElement.IMPORT_CONTAINER || isInnerType(je)) {
setExpanded(i, false);
return;
}
}
}
super.internalExpandToLevel(node, level);
}
/*
* @see org.eclipse.jface.viewers.AbstractTreeViewer#isExpandable(java.lang.Object)
*/
@Override
public boolean isExpandable(Object element) {
if (hasFilters()) {
return getFilteredChildren(element).length > 0;
}
return super.isExpandable(element);
}
/*
* @see ContentViewer#handleLabelProviderChanged(LabelProviderChangedEvent)
*/
@Override
protected void handleLabelProviderChanged(LabelProviderChangedEvent event) {
Object input= getInput();
if (event instanceof ProblemsLabelChangedEvent) {
ProblemsLabelChangedEvent e= (ProblemsLabelChangedEvent) event;
if (e.isMarkerChange() && input instanceof ICompilationUnit) {
return; // marker changes can be ignored
}
}
// look if the underlying resource changed
Object[] changed= event.getElements();
if (changed != null) {
IResource resource= getUnderlyingResource();
if (resource != null) {
for (int i= 0; i < changed.length; i++) {
if (changed[i] != null && changed[i].equals(resource)) {
// change event to a full refresh
event= new LabelProviderChangedEvent((IBaseLabelProvider) event.getSource());
break;
}
}
}
}
super.handleLabelProviderChanged(event);
}
private IResource getUnderlyingResource() {
Object input= getInput();
if (input instanceof ICompilationUnit) {
ICompilationUnit cu= (ICompilationUnit) input;
cu= cu.getPrimary();
return cu.getResource();
} else if (input instanceof IClassFile) {
return ((IClassFile) input).getResource();
}
return null;
}
}
class LexicalSortingAction extends Action {
private JavaElementComparator fComparator= new JavaElementComparator();
private SourcePositionComparator fSourcePositonComparator= new SourcePositionComparator();
public LexicalSortingAction() {
super();
PlatformUI.getWorkbench().getHelpSystem().setHelp(this, IJavaHelpContextIds.LEXICAL_SORTING_OUTLINE_ACTION);
setText(JavaEditorMessages.JavaOutlinePage_Sort_label);
JavaPluginImages.setLocalImageDescriptors(this, "alphab_sort_co.gif"); //$NON-NLS-1$
setToolTipText(JavaEditorMessages.JavaOutlinePage_Sort_tooltip);
setDescription(JavaEditorMessages.JavaOutlinePage_Sort_description);
boolean checked= JavaPlugin.getDefault().getPreferenceStore().getBoolean("LexicalSortingAction.isChecked"); //$NON-NLS-1$
valueChanged(checked, false);
}
@Override
public void run() {
valueChanged(isChecked(), true);
}
private void valueChanged(final boolean on, boolean store) {
setChecked(on);
BusyIndicator.showWhile(fOutlineViewer.getControl().getDisplay(), new Runnable() {
public void run() {
if (on) {
fOutlineViewer.setComparator(fComparator);
fDropSupport.setFeedbackEnabled(false);
} else {
fOutlineViewer.setComparator(fSourcePositonComparator);
fDropSupport.setFeedbackEnabled(true);
}
}
});
if (store)
JavaPlugin.getDefault().getPreferenceStore().setValue("LexicalSortingAction.isChecked", on); //$NON-NLS-1$
}
}
class ClassOnlyAction extends Action {
public ClassOnlyAction() {
super();
PlatformUI.getWorkbench().getHelpSystem().setHelp(this, IJavaHelpContextIds.GO_INTO_TOP_LEVEL_TYPE_ACTION);
setText(JavaEditorMessages.JavaOutlinePage_GoIntoTopLevelType_label);
setToolTipText(JavaEditorMessages.JavaOutlinePage_GoIntoTopLevelType_tooltip);
setDescription(JavaEditorMessages.JavaOutlinePage_GoIntoTopLevelType_description);
JavaPluginImages.setLocalImageDescriptors(this, "gointo_toplevel_type.gif"); //$NON-NLS-1$
IPreferenceStore preferenceStore= JavaPlugin.getDefault().getPreferenceStore();
boolean showclass= preferenceStore.getBoolean("GoIntoTopLevelTypeAction.isChecked"); //$NON-NLS-1$
setTopLevelTypeOnly(showclass);
}
/*
* @see org.eclipse.jface.action.Action#run()
*/
@Override
public void run() {
setTopLevelTypeOnly(!fTopLevelTypeOnly);
}
private void setTopLevelTypeOnly(boolean show) {
fTopLevelTypeOnly= show;
setChecked(show);
fOutlineViewer.refresh(false);
IPreferenceStore preferenceStore= JavaPlugin.getDefault().getPreferenceStore();
preferenceStore.setValue("GoIntoTopLevelTypeAction.isChecked", show); //$NON-NLS-1$
}
}
/**
* This action toggles whether this Java Outline page links
* its selection to the active editor.
*
* @since 3.0
*/
public class ToggleLinkingAction extends AbstractToggleLinkingAction {
/**
* Constructs a new action.
*/
public ToggleLinkingAction() {
boolean isLinkingEnabled= PreferenceConstants.getPreferenceStore().getBoolean(PreferenceConstants.EDITOR_SYNC_OUTLINE_ON_CURSOR_MOVE);
setChecked(isLinkingEnabled);
fOpenAndLinkWithEditorHelper.setLinkWithEditor(isLinkingEnabled);
}
/**
* Runs the action.
*/
@Override
public void run() {
final boolean isChecked= isChecked();
PreferenceConstants.getPreferenceStore().setValue(PreferenceConstants.EDITOR_SYNC_OUTLINE_ON_CURSOR_MOVE, isChecked);
if (isChecked && fEditor != null)
fEditor.synchronizeOutlinePage(fEditor.computeHighlightRangeSourceReference(), false);
fOpenAndLinkWithEditorHelper.setLinkWithEditor(isChecked);
}
}
/**
* Empty selection provider.
*
* @since 3.2
*/
private static final class EmptySelectionProvider implements ISelectionProvider {
public void addSelectionChangedListener(ISelectionChangedListener listener) {
}
public ISelection getSelection() {
return StructuredSelection.EMPTY;
}
public void removeSelectionChangedListener(ISelectionChangedListener listener) {
}
public void setSelection(ISelection selection) {
}
}
/**
* Formats the code associated with the elements selected in the Outline view. The action
* requires that the selection provided by the site's selection provider is of type
* {@link IStructuredSelection}
*
* @since 3.7
*/
private class FormatElementAction extends SelectionDispatchAction {
/**
* Creates a new <code>FormatViewElementAction</code>.
*
* @param site the site providing context information for this action
*/
FormatElementAction(IPageSite site) {
super(site);
}
/**
* Executes the action based on the Structured Selection. This formats the non-overlapping
* element(s) that have been selected in the view.
*
* @param selection the current selection
*/
@Override
public void run(IStructuredSelection selection) {
ICompilationUnit compilationUnit= (ICompilationUnit)((IJavaElement)selection.getFirstElement()).getAncestor(IJavaElement.COMPILATION_UNIT);
if (ElementValidator.check(compilationUnit, getShell(), JavaEditorMessages.JavaEditor_FormatElementDialog_label, fEditor != null)) {
JavaSourceViewer javaSourceViewer= (JavaSourceViewer)fEditor.getViewer();
javaSourceViewer.rememberSelection();
javaSourceViewer.setRedraw(false);
try {
IDocument document= javaSourceViewer.getDocument();
IRegion[] regions= getOrderedRegionsForNonOverlappingElements(selection, document);
Map<String, String> formatterSettings= new HashMap<String, String>(compilationUnit.getJavaProject().getOptions(true));
String content= compilationUnit.getBuffer().getContents();
String lineDelimiter= TextUtilities.getDefaultLineDelimiter(document);
TextEdit edit= CodeFormatterUtil.reformat(CodeFormatter.K_COMPILATION_UNIT | CodeFormatter.F_INCLUDE_COMMENTS, content, regions, 0, lineDelimiter, formatterSettings);
edit.apply(javaSourceViewer.getDocument());
} catch (CoreException e) {
JavaPlugin.log(e);
} catch (MalformedTreeException e) {
JavaPlugin.log(e);
} catch (BadLocationException e) {
JavaPlugin.log(e);
} finally {
javaSourceViewer.setRedraw(true);
javaSourceViewer.restoreSelection();
}
}
}
/**
* Parses the selections for non-overlapping elements and returns their source regions
* ordered by their offsets.
*
* @param selection the selected elements
* @param document the document containing the selected elements
* @return the array of ordered source regions
*/
private IRegion[] getOrderedRegionsForNonOverlappingElements(IStructuredSelection selection, IDocument document) {
List<?> allElements= selection.toList();
Iterator<?> iterator= selection.iterator();
ArrayList<IRegion> regions= new ArrayList<IRegion>(selection.size());
while (iterator.hasNext()) {
Object element= iterator.next();
if (!isElementOverlapping((IJavaElement)element, allElements)) {
regions.add(getElementRegion(element, document));
}
}
Comparator<IRegion> comparator= new Comparator<IRegion>() {
public int compare(IRegion region0, IRegion region1) {
int region1Offset= region0.getOffset();
int region2Offset= region1.getOffset();
if (region1Offset > region2Offset)
return 1;
else if (region1Offset == region2Offset)
return 0;
else
return -1;
}
};
Collections.sort(regions, comparator);
Object[] sortedObjects= regions.toArray();
IRegion[] sortedRegions= new Region[sortedObjects.length];
System.arraycopy(sortedObjects, 0, sortedRegions, 0, sortedObjects.length);
return sortedRegions;
}
/**
* Calculates the region of the element. The start is at beginning of its first line if from
* the source start to the beginning of the line is all whitespace.
*
* @param element the element whose regions is to be calculated
* @param document the document containing the element whose region is to be calculated
* @return the region for the element
*/
private Region getElementRegion(Object element, IDocument document) {
try {
ISourceRange sourceRange= ((ISourceReference)element).getSourceRange();
int sourceOffset= sourceRange.getOffset();
int beginningOfWSOffset= sourceOffset - 1;
int lineAtSourceOffset= document.getLineOfOffset(sourceOffset);
while (beginningOfWSOffset >= 0 && Character.isWhitespace(document.getChar(beginningOfWSOffset)) && lineAtSourceOffset == document.getLineOfOffset(beginningOfWSOffset)) {
beginningOfWSOffset--;
}
beginningOfWSOffset++;
int sourceLength= sourceRange.getLength() + (sourceOffset - beginningOfWSOffset);
if (lineAtSourceOffset != document.getLineOfOffset(beginningOfWSOffset))
return new Region(document.getLineOffset(lineAtSourceOffset), sourceLength);
else
return new Region(beginningOfWSOffset, sourceLength);
} catch (JavaModelException e) {
JavaPlugin.log(e);
} catch (BadLocationException e) {
JavaPlugin.log(e);
}
return null;
}
/**
* Checks if element has an enclosing parent among other selected elements.
*
* @param element the element to be checked for overlap against all elements
* @param allElements the list of all elements
* @return <code>true</code> if the element has a parent in the list of all elements
*/
private boolean isElementOverlapping(IJavaElement element, List<?> allElements) {
element= element.getParent();
while (element != null) {
if (element instanceof ISourceReference) {
if (allElements.contains(element))
return true;
} else {
return false;
}
element= element.getParent();
}
return false;
}
/**
* Notifies the action of a change in the Selection.
*
* @param selection the new Structured Selection
*/
@Override
public void selectionChanged(IStructuredSelection selection) {
setEnabled(fEditor.isEditorInputModifiable());
}
}
/** A flag to show contents of top level type only */
private boolean fTopLevelTypeOnly;
private IJavaElement fInput;
private String fContextMenuID;
private Menu fMenu;
private JavaOutlineViewer fOutlineViewer;
private JavaEditor fEditor;
private MemberFilterActionGroup fMemberFilterActionGroup;
private ListenerList fSelectionChangedListeners= new ListenerList(ListenerList.IDENTITY);
private ListenerList fPostSelectionChangedListeners= new ListenerList(ListenerList.IDENTITY);
private Hashtable<String, IAction> fActions= new Hashtable<String, IAction>();
private TogglePresentationAction fTogglePresentation;
private ToggleLinkingAction fToggleLinkingAction;
/**
* Action for Collapse All.
*
* @since 3.7
*/
private CollapseAllAction fCollapseAllAction;
/**
* Action for Format Element
*
* @since 3.7
*/
private FormatElementAction fFormatElement;
private CompositeActionGroup fActionGroups;
private IPropertyChangeListener fPropertyChangeListener;
/**
* Custom filter action group.
* @since 3.0
*/
private CustomFiltersActionGroup fCustomFiltersActionGroup;
/**
* Category filter action group.
* @since 3.2
*/
private CategoryFilterActionGroup fCategoryFilterActionGroup;
private JdtViewerDropSupport fDropSupport;
/**
* Helper to open and activate editors.
* @since 3.5
*/
private OpenAndLinkWithEditorHelper fOpenAndLinkWithEditorHelper;
public JavaOutlinePage(String contextMenuID, JavaEditor editor) {
super();
Assert.isNotNull(editor);
fContextMenuID= contextMenuID;
fEditor= editor;
fTogglePresentation= new TogglePresentationAction();
fTogglePresentation.setEditor(editor);
fPropertyChangeListener= new IPropertyChangeListener() {
public void propertyChange(PropertyChangeEvent event) {
doPropertyChange(event);
}
};
JavaPlugin.getDefault().getPreferenceStore().addPropertyChangeListener(fPropertyChangeListener);
}
/*
* @see org.eclipse.ui.part.Page#init(org.eclipse.ui.part.IPageSite)
*/
@Override
public void init(IPageSite pageSite) {
super.init(pageSite);
}
private void doPropertyChange(PropertyChangeEvent event) {
if (fOutlineViewer != null) {
if (MembersOrderPreferenceCache.isMemberOrderProperty(event.getProperty())) {
fOutlineViewer.refresh(false);
}
}
}
/*
* @see ISelectionProvider#addSelectionChangedListener(ISelectionChangedListener)
*/
public void addSelectionChangedListener(ISelectionChangedListener listener) {
if (fOutlineViewer != null)
fOutlineViewer.addSelectionChangedListener(listener);
else
fSelectionChangedListeners.add(listener);
}
/*
* @see ISelectionProvider#removeSelectionChangedListener(ISelectionChangedListener)
*/
public void removeSelectionChangedListener(ISelectionChangedListener listener) {
if (fOutlineViewer != null)
fOutlineViewer.removeSelectionChangedListener(listener);
else
fSelectionChangedListeners.remove(listener);
}
/*
* @see ISelectionProvider#setSelection(ISelection)
*/
public void setSelection(ISelection selection) {
if (fOutlineViewer != null)
fOutlineViewer.setSelection(selection);
}
/*
* @see ISelectionProvider#getSelection()
*/
public ISelection getSelection() {
if (fOutlineViewer == null)
return StructuredSelection.EMPTY;
return fOutlineViewer.getSelection();
}
/*
* @see org.eclipse.jface.text.IPostSelectionProvider#addPostSelectionChangedListener(org.eclipse.jface.viewers.ISelectionChangedListener)
*/
public void addPostSelectionChangedListener(ISelectionChangedListener listener) {
if (fOutlineViewer != null)
fOutlineViewer.addPostSelectionChangedListener(listener);
else
fPostSelectionChangedListeners.add(listener);
}
/*
* @see org.eclipse.jface.text.IPostSelectionProvider#removePostSelectionChangedListener(org.eclipse.jface.viewers.ISelectionChangedListener)
*/
public void removePostSelectionChangedListener(ISelectionChangedListener listener) {
if (fOutlineViewer != null)
fOutlineViewer.removePostSelectionChangedListener(listener);
else
fPostSelectionChangedListeners.remove(listener);
}
private void registerToolbarActions(IActionBars actionBars) {
IToolBarManager toolBarManager= actionBars.getToolBarManager();
fCollapseAllAction= new CollapseAllAction(fOutlineViewer);
fCollapseAllAction.setActionDefinitionId(CollapseAllHandler.COMMAND_ID);
toolBarManager.add(fCollapseAllAction);
toolBarManager.add(new LexicalSortingAction());
fMemberFilterActionGroup= new MemberFilterActionGroup(fOutlineViewer, "org.eclipse.jdt.ui.JavaOutlinePage"); //$NON-NLS-1$
fMemberFilterActionGroup.contributeToToolBar(toolBarManager);
fCustomFiltersActionGroup.fillActionBars(actionBars);
IMenuManager viewMenuManager= actionBars.getMenuManager();
viewMenuManager.add(new Separator("EndFilterGroup")); //$NON-NLS-1$
fToggleLinkingAction= new ToggleLinkingAction();
fToggleLinkingAction.setActionDefinitionId(IWorkbenchCommandConstants.NAVIGATE_TOGGLE_LINK_WITH_EDITOR);
viewMenuManager.add(new ClassOnlyAction());
viewMenuManager.add(fToggleLinkingAction);
fCategoryFilterActionGroup= new CategoryFilterActionGroup(fOutlineViewer, "org.eclipse.jdt.ui.JavaOutlinePage", new IJavaElement[] {fInput}); //$NON-NLS-1$
fCategoryFilterActionGroup.contributeToViewMenu(viewMenuManager);
}
/*
* @see IPage#createControl
*/
@Override
public void createControl(Composite parent) {
Tree tree= new Tree(parent, SWT.MULTI);
AppearanceAwareLabelProvider lprovider= new AppearanceAwareLabelProvider(
AppearanceAwareLabelProvider.DEFAULT_TEXTFLAGS | JavaElementLabels.F_APP_TYPE_SIGNATURE | JavaElementLabels.ALL_CATEGORY,
AppearanceAwareLabelProvider.DEFAULT_IMAGEFLAGS
);
fOutlineViewer= new JavaOutlineViewer(tree);
initDragAndDrop();
fOutlineViewer.setContentProvider(new ChildrenProvider());
fOutlineViewer.setLabelProvider(new DecoratingJavaLabelProvider(lprovider));
Object[] listeners= fSelectionChangedListeners.getListeners();
for (int i= 0; i < listeners.length; i++) {
fSelectionChangedListeners.remove(listeners[i]);
fOutlineViewer.addSelectionChangedListener((ISelectionChangedListener) listeners[i]);
}
listeners= fPostSelectionChangedListeners.getListeners();
for (int i= 0; i < listeners.length; i++) {
fPostSelectionChangedListeners.remove(listeners[i]);
fOutlineViewer.addPostSelectionChangedListener((ISelectionChangedListener) listeners[i]);
}
MenuManager manager= new MenuManager(fContextMenuID, fContextMenuID);
manager.setRemoveAllWhenShown(true);
manager.addMenuListener(new IMenuListener() {
public void menuAboutToShow(IMenuManager m) {
contextMenuAboutToShow(m);
}
});
fMenu= manager.createContextMenu(tree);
tree.setMenu(fMenu);
IPageSite site= getSite();
site.registerContextMenu(JavaPlugin.getPluginId() + ".outline", manager, fOutlineViewer); //$NON-NLS-1$
updateSelectionProvider(site);
// we must create the groups after we have set the selection provider to the site
fActionGroups= new CompositeActionGroup(new ActionGroup[] {
new OpenViewActionGroup(this),
new CCPActionGroup(this),
new GenerateActionGroup(this),
new RefactorActionGroup(this),
new JavaSearchActionGroup(this)});
// register global actions
IActionBars actionBars= site.getActionBars();
actionBars.setGlobalActionHandler(ITextEditorActionConstants.UNDO, fEditor.getAction(ITextEditorActionConstants.UNDO));
actionBars.setGlobalActionHandler(ITextEditorActionConstants.REDO, fEditor.getAction(ITextEditorActionConstants.REDO));
IAction action= fEditor.getAction(ITextEditorActionConstants.NEXT);
actionBars.setGlobalActionHandler(ITextEditorActionDefinitionIds.GOTO_NEXT_ANNOTATION, action);
actionBars.setGlobalActionHandler(ITextEditorActionConstants.NEXT, action);
action= fEditor.getAction(ITextEditorActionConstants.PREVIOUS);
actionBars.setGlobalActionHandler(ITextEditorActionDefinitionIds.GOTO_PREVIOUS_ANNOTATION, action);
actionBars.setGlobalActionHandler(ITextEditorActionConstants.PREVIOUS, action);
actionBars.setGlobalActionHandler(ITextEditorActionDefinitionIds.TOGGLE_SHOW_SELECTED_ELEMENT_ONLY, fTogglePresentation);
fActionGroups.fillActionBars(actionBars);
fFormatElement= new FormatElementAction(site);
fFormatElement.setActionDefinitionId(IJavaEditorActionDefinitionIds.QUICK_FORMAT);
site.getSelectionProvider().addSelectionChangedListener(fFormatElement);
actionBars.setGlobalActionHandler(JdtActionConstants.FORMAT_ELEMENT, fFormatElement);
IStatusLineManager statusLineManager= actionBars.getStatusLineManager();
if (statusLineManager != null) {
StatusBarUpdater updater= new StatusBarUpdater(statusLineManager);
fOutlineViewer.addPostSelectionChangedListener(updater);
}
// Custom filter group
fCustomFiltersActionGroup= new CustomFiltersActionGroup("org.eclipse.jdt.ui.JavaOutlinePage", fOutlineViewer); //$NON-NLS-1$
fOpenAndLinkWithEditorHelper= new OpenAndLinkWithEditorHelper(fOutlineViewer) {
@Override
protected void activate(ISelection selection) {
fEditor.doSelectionChanged(selection);
getSite().getPage().activate(fEditor);
}
@Override
protected void linkToEditor(ISelection selection) {
fEditor.doSelectionChanged(selection);
}
@Override
protected void open(ISelection selection, boolean activate) {
fEditor.doSelectionChanged(selection);
if (activate)
getSite().getPage().activate(fEditor);
}
};
registerToolbarActions(actionBars);
IHandlerService handlerService= site.getService(IHandlerService.class);
handlerService.activateHandler(IWorkbenchCommandConstants.NAVIGATE_TOGGLE_LINK_WITH_EDITOR, new ActionHandler(fToggleLinkingAction));
handlerService.activateHandler(CollapseAllHandler.COMMAND_ID, new ActionHandler(fCollapseAllAction));
fOutlineViewer.setInput(fInput);
}
/*
* @since 3.2
*/
private void updateSelectionProvider(IPageSite site) {
ISelectionProvider provider= fOutlineViewer;
if (fInput != null) {
ICompilationUnit cu= (ICompilationUnit)fInput.getAncestor(IJavaElement.COMPILATION_UNIT);
if (cu != null && !JavaModelUtil.isPrimary(cu))
provider= new EmptySelectionProvider();
}
site.setSelectionProvider(provider);
}
@Override
public void dispose() {
if (fEditor == null)
return;
if (fMemberFilterActionGroup != null) {
fMemberFilterActionGroup.dispose();
fMemberFilterActionGroup= null;
}
if (fCategoryFilterActionGroup != null) {
fCategoryFilterActionGroup.dispose();
fCategoryFilterActionGroup= null;
}
if (fCustomFiltersActionGroup != null) {
fCustomFiltersActionGroup.dispose();
fCustomFiltersActionGroup= null;
}
fEditor.outlinePageClosed();
fEditor= null;
fSelectionChangedListeners.clear();
fSelectionChangedListeners= null;
getSite().getSelectionProvider().removeSelectionChangedListener(fFormatElement);
fPostSelectionChangedListeners.clear();
fPostSelectionChangedListeners= null;
if (fPropertyChangeListener != null) {
JavaPlugin.getDefault().getPreferenceStore().removePropertyChangeListener(fPropertyChangeListener);
fPropertyChangeListener= null;
}
if (fMenu != null && !fMenu.isDisposed()) {
fMenu.dispose();
fMenu= null;
}
if (fActionGroups != null)
fActionGroups.dispose();
fTogglePresentation.setEditor(null);
fOutlineViewer= null;
super.dispose();
}
@Override
public Control getControl() {
if (fOutlineViewer != null)
return fOutlineViewer.getControl();
return null;
}
public void setInput(IJavaElement inputElement) {
fInput= inputElement;
if (fOutlineViewer != null) {
fOutlineViewer.setInput(fInput);
updateSelectionProvider(getSite());
}
if (fCategoryFilterActionGroup != null)
fCategoryFilterActionGroup.setInput(new IJavaElement[] {fInput});
}
public void select(ISourceReference reference) {
if (fOutlineViewer != null) {
ISelection s= fOutlineViewer.getSelection();
if (s instanceof IStructuredSelection) {
IStructuredSelection ss= (IStructuredSelection) s;
List<?> elements= ss.toList();
if (!elements.contains(reference)) {
s= (reference == null ? StructuredSelection.EMPTY : new StructuredSelection(reference));
fOutlineViewer.setSelection(s, true);
}
}
}
}
public void setAction(String actionID, IAction action) {
Assert.isNotNull(actionID);
if (action == null)
fActions.remove(actionID);
else
fActions.put(actionID, action);
}
public IAction getAction(String actionID) {
Assert.isNotNull(actionID);
return fActions.get(actionID);
}
@SuppressWarnings("unchecked")
public <T> T getAdapter(Class<T> key) {
if (key == IShowInSource.class) {
return (T) getShowInSource();
}
if (key == IShowInTargetList.class) {
return (T) new IShowInTargetList() {
public String[] getShowInTargetIds() {
return new String[] { JavaUI.ID_PACKAGES };
}
};
}
if (key == IShowInTarget.class) {
return (T) getShowInTarget();
}
return null;
}
/**
* Convenience method to add the action installed under the given actionID to the
* specified group of the menu.
*
* @param menu the menu manager
* @param group the group to which to add the action
* @param actionID the ID of the new action
*/
protected void addAction(IMenuManager menu, String group, String actionID) {
IAction action= getAction(actionID);
if (action != null) {
if (action instanceof IUpdate)
((IUpdate) action).update();
if (action.isEnabled()) {
IMenuManager subMenu= menu.findMenuUsingPath(group);
if (subMenu != null)
subMenu.add(action);
else
menu.appendToGroup(group, action);
}
}
}
protected void contextMenuAboutToShow(IMenuManager menu) {
JavaPlugin.createStandardGroups(menu);
IStructuredSelection selection= (IStructuredSelection)getSelection();
fActionGroups.setContext(new ActionContext(selection));
fActionGroups.fillContextMenu(menu);
}
/*
* @see Page#setFocus()
*/
@Override
public void setFocus() {
if (fOutlineViewer != null)
fOutlineViewer.getControl().setFocus();
}
/**
* Checks whether a given Java element is an inner type.
*
* @param element the java element
* @return <code>true</code> iff the given element is an inner type
*/
private boolean isInnerType(IJavaElement element) {
if (element != null && element.getElementType() == IJavaElement.TYPE) {
IType type= (IType)element;
try {
return type.isMember();
} catch (JavaModelException e) {
IJavaElement parent= type.getParent();
if (parent != null) {
int parentElementType= parent.getElementType();
return (parentElementType != IJavaElement.COMPILATION_UNIT && parentElementType != IJavaElement.CLASS_FILE);
}
}
}
return false;
}
/**
* Returns the <code>IShowInSource</code> for this view.
*
* @return the {@link IShowInSource}
*/
protected IShowInSource getShowInSource() {
return new IShowInSource() {
public ShowInContext getShowInContext() {
return new ShowInContext(
null,
getSite().getSelectionProvider().getSelection());
}
};
}
/**
* Returns the <code>IShowInTarget</code> for this view.
*
* @return the {@link IShowInTarget}
*/
protected IShowInTarget getShowInTarget() {
return new IShowInTarget() {
public boolean show(ShowInContext context) {
ISelection sel= context.getSelection();
if (sel instanceof ITextSelection) {
ITextSelection tsel= (ITextSelection) sel;
int offset= tsel.getOffset();
IJavaElement element= fEditor.getElementAt(offset);
if (element != null) {
setSelection(new StructuredSelection(element));
return true;
}
} else if (sel instanceof IStructuredSelection) {
setSelection(sel);
return true;
}
return false;
}
};
}
private void initDragAndDrop() {
fDropSupport= new JdtViewerDropSupport(fOutlineViewer);
fDropSupport.start();
new JdtViewerDragSupport(fOutlineViewer).start();
}
/**
* Returns whether only the contents of the top level type is to be shown.
*
* @return <code>true</code> if only the contents of the top level type is to be shown.
* @since 3.3
*/
protected final boolean isTopLevelTypeOnly() {
return fTopLevelTypeOnly;
}
/**
* Returns the <code>JavaOutlineViewer</code> of this view.
*
* @return the {@link JavaOutlineViewer}
* @since 3.3
*/
protected final JavaOutlineViewer getOutlineViewer() {
return fOutlineViewer;
}
}