/*******************************************************************************
* Copyright (c) 2006, 2008 Spring IDE Developers
* 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:
* Spring IDE Developers - initial API and implementation
*******************************************************************************/
package org.springframework.ide.eclipse.aop.ui.navigator;
import org.eclipse.jdt.core.IField;
import org.eclipse.jdt.core.IJavaElement;
import org.eclipse.jdt.core.IMethod;
import org.eclipse.jdt.core.IType;
import org.eclipse.jface.preference.IPreferenceStore;
import org.eclipse.jface.viewers.AbstractTreeViewer;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.viewers.StructuredSelection;
import org.eclipse.jface.viewers.TreeViewer;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.TreeItem;
import org.eclipse.ui.IActionBars;
import org.eclipse.ui.IPropertyListener;
import org.eclipse.ui.ISelectionListener;
import org.eclipse.ui.IWorkbenchPart;
import org.eclipse.ui.navigator.CommonNavigator;
import org.eclipse.wst.sse.core.internal.provisional.text.IStructuredDocument;
import org.eclipse.wst.xml.core.internal.document.ElementImpl;
import org.springframework.ide.eclipse.aop.ui.Activator;
import org.springframework.ide.eclipse.aop.ui.navigator.action.ToggleShowBeanRefsForFileAction;
import org.springframework.ide.eclipse.aop.ui.navigator.model.AbstractJavaElementReferenceNode;
import org.springframework.ide.eclipse.aop.ui.navigator.model.AdviceAopReferenceNode;
import org.springframework.ide.eclipse.aop.ui.navigator.model.AdviceDeclareParentAopReferenceNode;
import org.springframework.ide.eclipse.aop.ui.navigator.model.AdvisedAopReferenceNode;
import org.springframework.ide.eclipse.aop.ui.navigator.model.AdvisedDeclareParentAopReferenceNode;
import org.springframework.ide.eclipse.aop.ui.navigator.model.IRevealableReferenceNode;
import org.springframework.ide.eclipse.aop.ui.navigator.util.AopReferenceModelNavigatorUtils;
import org.springframework.ide.eclipse.beans.core.BeansCorePlugin;
import org.springframework.ide.eclipse.core.model.IModelChangeListener;
import org.springframework.ide.eclipse.core.model.ModelChangeEvent;
import org.springframework.ide.eclipse.core.model.ModelChangeEvent.Type;
import org.springframework.ide.eclipse.ui.dialogs.WrappingStructuredSelection;
import org.w3c.dom.Element;
/**
* Customized extension of the {@link CommonNavigator} that links to the current
* selected element in the Java Editor or Spring IDE's XML Editor.
* @author Christian Dupuis
* @author Torsten Juergeleit
* @since 2.0
*/
@SuppressWarnings("restriction")
public class AopReferenceModelNavigator extends CommonNavigator implements
ISelectionListener, IModelChangeListener {
public static final String ID = "org.springframework.ide.eclipse.aop.ui.navigator.aopReferenceModelNavigator";
public static final String BEAN_REFS_FOR_FILE_ID = ID + ".beanRefsForFile";
static {
IPreferenceStore pstore = Activator.getDefault().getPreferenceStore();
showBeansRefsForFileEnabled = pstore.getBoolean(BEAN_REFS_FOR_FILE_ID);
}
private static boolean showBeansRefsForFileEnabled;
public static int calculateExpandToLevel(Object element) {
return AbstractTreeViewer.ALL_LEVELS;
}
public static Object calculateRootElement(Object element) {
return calculateRootElement(element, showBeansRefsForFileEnabled);
}
public static Object calculateRootElement(Object element,
boolean showBeansRefsForFile) {
if (showBeansRefsForFile && element != null) {
if (element instanceof IMethod) {
element = ((IMethod) element).getDeclaringType();
}
else if (element instanceof Element) {
element = ((Element) element).getOwnerDocument()
.getDocumentElement();
}
else if (element instanceof IField) {
element = ((IField) element).getDeclaringType();
}
}
return element;
}
public static void expandTree(TreeItem[] items, boolean b) {
if (items != null) {
for (TreeItem item : items) {
Object obj = item.getData();
if (obj instanceof AdviceAopReferenceNode
|| obj instanceof AdvisedAopReferenceNode
|| obj instanceof AdviceDeclareParentAopReferenceNode
|| obj instanceof AdvisedDeclareParentAopReferenceNode) {
expandTree(item.getItems(), true);
}
else {
expandTree(item.getItems(), b);
}
if (b) {
item.setExpanded(false);
}
}
}
}
public static void refreshViewer(TreeViewer viewer,
final Object rootElement, Object element) {
viewer.getTree().setRedraw(false);
viewer.setInput(rootElement);
// viewer.refresh();
viewer.expandToLevel(calculateExpandToLevel(rootElement));
expandTree(viewer.getTree().getItems(), false);
revealSelection(viewer, element);
viewer.getTree().setRedraw(true);
}
public static void revealSelection(TreeViewer viewer,
final Object javaElement) {
revealSelection(viewer, javaElement, showBeansRefsForFileEnabled);
}
public static void revealSelection(TreeViewer viewer,
final Object javaElement, boolean showBeansRefsForFile) {
TreeItem[] items = viewer.getTree().getItems();
Object wr = null;
if (javaElement instanceof IJavaElement) {
if (showBeansRefsForFile && javaElement instanceof IMethod) {
// we have one root element
TreeItem[] aspectChildren = items[0].getItems();
for (TreeItem element : aspectChildren) {
Object obj = element.getData();
if (obj instanceof AbstractJavaElementReferenceNode
&& ((AbstractJavaElementReferenceNode) obj)
.getElement().equals(javaElement)) {
wr = obj;
break;
}
}
}
else {
if (items != null && items.length > 0) {
wr = items[0].getData();
}
}
}
else if (javaElement instanceof ElementImpl) {
if (!showBeansRefsForFile) {
if (items != null && items.length > 0) {
wr = items[0].getData();
}
}
else {
ElementImpl element = (ElementImpl) javaElement;
IStructuredDocument document = element.getStructuredDocument();
int startLine = document.getLineOfOffset(element
.getStartOffset()) + 1;
int endLine = document.getLineOfOffset(element.getEndOffset()) + 1;
for (TreeItem element0 : items) {
Object obj = element0.getData();
if (obj instanceof IRevealableReferenceNode
&& ((IRevealableReferenceNode) obj).getLineNumber() >= startLine
&& ((IRevealableReferenceNode) obj).getLineNumber() <= endLine) {
wr = obj;
break;
}
}
}
}
if (wr != null) {
viewer.setSelection(new StructuredSelection(wr), true);
viewer.reveal(wr);
}
}
private Object lastElement;
private ISelection lastSelection;
private IWorkbenchPart lastWorkbenchPart;
private ToggleShowBeanRefsForFileAction toggleShowBeanRefsForFileAction;
private IPropertyListener propertyListener;
@Override
public void createPartControl(Composite aParent) {
super.createPartControl(aParent);
getSite().getWorkbenchWindow().getSelectionService()
.addPostSelectionListener(this);
BeansCorePlugin.getModel().addChangeListener(this);
propertyListener = new IPropertyListener() {
public void propertyChanged(Object source, int propId) {
if (propId == IS_LINKING_ENABLED_PROPERTY) {
updateTreeViewer(lastWorkbenchPart, lastSelection, false);
}
}
};
addPropertyListener(propertyListener);
makeActions();
}
@Override
public void dispose() {
removePropertyListener(propertyListener);
super.dispose();
BeansCorePlugin.getModel().removeChangeListener(this);
getSite().getWorkbenchWindow().getSelectionService()
.removeSelectionListener(this);
}
public boolean isShowBeansRefsForFileEnabled() {
return showBeansRefsForFileEnabled;
}
private void makeActions() {
this.toggleShowBeanRefsForFileAction = new ToggleShowBeanRefsForFileAction(
this);
IActionBars bars = getViewSite().getActionBars();
bars.getToolBarManager().add(toggleShowBeanRefsForFileAction);
bars.getMenuManager().add(toggleShowBeanRefsForFileAction);
}
public void selectionChanged(IWorkbenchPart part, ISelection selection) {
if (selection instanceof IStructuredSelection) {
selection = new WrappingStructuredSelection(
(IStructuredSelection) selection);
}
updateTreeViewer(part, selection, true);
}
public void setShowBeansRefsForFileEnabled(
boolean showBeansRefsForFileEnabled) {
AopReferenceModelNavigator.showBeansRefsForFileEnabled = showBeansRefsForFileEnabled;
IPreferenceStore pstore = Activator.getDefault().getPreferenceStore();
pstore.setValue(BEAN_REFS_FOR_FILE_ID, showBeansRefsForFileEnabled);
Activator.getDefault().savePluginPreferences();
updateTreeViewer(lastWorkbenchPart, lastSelection, false);
}
private void updateTreeViewer(final IWorkbenchPart part, final ISelection selection,
final boolean ignoreSameSelection) {
// Abort if this happens after disposes
Control ctrl = getCommonViewer().getControl();
if (ctrl == null || ctrl.isDisposed()) {
return;
}
// Are we in the UI thread?
if (ctrl.getDisplay().getThread() == Thread.currentThread()) {
determineAndRefreshViewer(part, selection, ignoreSameSelection);
}
else {
ctrl.getDisplay().asyncExec(new Runnable() {
public void run() {
// Abort if this happens after disposes
Control ctrl = getCommonViewer().getControl();
if (ctrl == null || ctrl.isDisposed()) {
return;
}
determineAndRefreshViewer(part, selection,
ignoreSameSelection);
}
});
}
}
private void determineAndRefreshViewer(IWorkbenchPart part,
ISelection selection, boolean ignoreSameSelection) {
final Object element = AopReferenceModelNavigatorUtils
.getSelectedElement(part, selection);
if (element == null
|| (element.equals(lastElement) && ignoreSameSelection)) {
return;
}
if ((element instanceof IType || element instanceof IMethod
|| element instanceof IField || element instanceof Element)
&& isLinkingEnabled()) {
refreshViewer(getCommonViewer(), calculateRootElement(element,
showBeansRefsForFileEnabled), element);
lastElement = element;
lastSelection = selection;
lastWorkbenchPart = part;
}
}
/**
* Refreshed the view on added and changed events so that no closed project
* artefacts will be displayed in the references view.
*/
public void elementChanged(ModelChangeEvent event) {
if (event == null || event.getType() == Type.ADDED || event.getType() == Type.REMOVED) {
updateTreeViewer(lastWorkbenchPart, lastSelection, false);
}
}
}