org.springframework.ide.eclipse.ui.navigator.SpringNavigator.java Source code

Java tutorial

Introduction

Here is the source code for org.springframework.ide.eclipse.ui.navigator.SpringNavigator.java

Source

/*******************************************************************************
 * Copyright (c) 2008, 2009 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.ui.navigator;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;

import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IMarker;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.jdt.core.ICompilationUnit;
import org.eclipse.jdt.core.IField;
import org.eclipse.jdt.core.IImportContainer;
import org.eclipse.jdt.core.IImportDeclaration;
import org.eclipse.jdt.core.IJavaElement;
import org.eclipse.jdt.core.IJavaModelMarker;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.IMethod;
import org.eclipse.jdt.core.ISourceRange;
import org.eclipse.jdt.core.ISourceReference;
import org.eclipse.jdt.core.IType;
import org.eclipse.jdt.core.JavaModelException;
import org.eclipse.jdt.internal.core.SourceType;
import org.eclipse.jdt.internal.ui.JavaPlugin;
import org.eclipse.jdt.internal.ui.javaeditor.JavaEditor;
import org.eclipse.jdt.ui.IWorkingCopyManager;
import org.eclipse.jface.text.ITextSelection;
import org.eclipse.jface.text.ITextViewerExtension5;
import org.eclipse.jface.text.source.ISourceViewer;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.viewers.TreeViewer;
import org.eclipse.swt.custom.StyledText;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.ui.IEditorPart;
import org.eclipse.ui.IPropertyListener;
import org.eclipse.ui.ISelectionListener;
import org.eclipse.ui.IWorkbenchPart;
import org.eclipse.ui.internal.navigator.NavigatorContentService;
import org.eclipse.ui.navigator.CommonNavigator;
import org.eclipse.ui.navigator.ILinkHelper;
import org.springframework.ide.eclipse.core.model.IModelElement;
import org.springframework.ide.eclipse.ui.SpringUIPlugin;
import org.springframework.ide.eclipse.ui.dialogs.WrappingStructuredSelection;
import org.springframework.ide.eclipse.ui.navigator.actions.ILinkHelperExtension;
import org.w3c.dom.Comment;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.Text;

/**
 * {@link CommonNavigator} extension that supports a special "Link to Editor mode" that selects
 * {@link IModelElement} instances based on selections in the XML files.
 * <p>
 * Actual resolution of {@link Element} instances to {@link IModelElement} instances is delegated to
 * implementations of the {@link ILinkHelperExtension} interface. Those implementations can be
 * contributed as usual by using the link helper content contribution of the common navigator
 * framework.
 * @author Christian Dupuis
 * @since 2.2.0
 */
@SuppressWarnings("restriction")
public final class SpringNavigator extends CommonNavigator implements ISelectionListener {

    /** Mapping between working copy managers and open editors */
    private static Map<Object, Object> workingCopyManagersForEditors = new HashMap<Object, Object>();

    /**
     * Last selected element; stored in order to prevent updating on selecting the same element
     * again
     */
    private ISelection lastElement;

    private Object linkService;

    private IPropertyListener propertyListener;

    private Method linkServiceMethod;

    /**
     * Register the {@link ISelectionListener} with the workbench.
     */
    @Override
    public void createPartControl(Composite aParent) {
        super.createPartControl(aParent);
        getSite().getWorkbenchWindow().getSelectionService().addPostSelectionListener(this);
        propertyListener = new IPropertyListener() {

            public void propertyChanged(Object source, int propId) {
                if (propId == IS_LINKING_ENABLED_PROPERTY) {
                    updateTreeViewer(SpringNavigator.this, lastElement, false);
                }
            }

        };
        addPropertyListener(propertyListener);

    }

    /**
     * Remove the {@link ISelectionListener} from the workbench.
     */
    @Override
    public void dispose() {
        getSite().getWorkbenchWindow().getSelectionService().removeSelectionListener(this);
        removePropertyListener(propertyListener);
        super.dispose();
    }

    /**
     * {@inheritDoc}
     */
    public void selectionChanged(IWorkbenchPart part, ISelection selection) {
        if (selection instanceof IStructuredSelection) {
            selection = new WrappingStructuredSelection((IStructuredSelection) selection);
        }
        updateTreeViewer(part, selection, true);
    }

    /**
     * Computes and returns the source reference. This is taken from the
     * computeHighlightRangeSourceReference() method in the JavaEditor class which is used to
     * populate the outline view
     * @return the computed source reference
     */
    private ISourceReference computeHighlightRangeSourceReference(JavaEditor editor) {
        ISourceViewer sourceViewer = editor.getViewer();
        if (sourceViewer == null)
            return null;

        StyledText styledText = sourceViewer.getTextWidget();
        if (styledText == null)
            return null;

        int caret = 0;
        if (sourceViewer instanceof ITextViewerExtension5) {
            ITextViewerExtension5 extension = (ITextViewerExtension5) sourceViewer;
            caret = extension.widgetOffset2ModelOffset(styledText.getCaretOffset());
        } else {
            int offset = sourceViewer.getVisibleRegion().getOffset();
            caret = offset + styledText.getCaretOffset();
        }

        IJavaElement element = getElementAt(editor, caret, false);

        if (!(element instanceof ISourceReference))
            return null;

        if (element.getElementType() == IJavaElement.IMPORT_DECLARATION) {

            IImportDeclaration declaration = (IImportDeclaration) element;
            IImportContainer container = (IImportContainer) declaration.getParent();
            ISourceRange srcRange = null;
            try {
                srcRange = container.getSourceRange();
            } catch (JavaModelException e) {
            }

            if (srcRange != null && srcRange.getOffset() == caret)
                return container;
        }

        return (ISourceReference) element;
    }

    private void determineAndRefreshViewer(IWorkbenchPart part, ISelection selection, boolean ignoreSameSelection) {
        final Object element = getSelectedElement(part, selection);
        if (element == null || (element.equals(lastElement) && ignoreSameSelection)) {
            return;
        }
        if ((element instanceof IType || element instanceof IMethod || element instanceof IField
                || element instanceof Element || element instanceof IResource) && isLinkingEnabled()) {
            selectReveal(getCommonViewer(), element);
        }
        lastElement = selection;
    }

    /**
     * Returns the most narrow java element including the given offset. This is taken from the
     * getElementAt(int offset, boolean reconcile) method in the CompilationUnitEditor class.
     */
    private IJavaElement getElementAt(JavaEditor editor, int offset, boolean reconcile) {
        IWorkingCopyManager manager;
        if (workingCopyManagersForEditors.get(editor) instanceof IWorkingCopyManager) {
            manager = (IWorkingCopyManager) workingCopyManagersForEditors.get(editor);
        } else {
            manager = JavaPlugin.getDefault().getWorkingCopyManager();
        }
        ICompilationUnit unit = manager.getWorkingCopy(editor.getEditorInput());

        if (unit != null) {
            try {
                if (reconcile) {
                    synchronized (unit) {
                        unit.reconcile(ICompilationUnit.NO_AST, false, null, null);
                    }
                    IJavaElement elementAt = unit.getElementAt(offset);
                    if (elementAt != null) {
                        return elementAt;
                    }
                    // this is if the selection in the editor
                    // is outside the {} of the class or aspect
                    IJavaElement[] children = unit.getChildren();
                    for (IJavaElement element : children) {
                        if (element instanceof SourceType) {
                            return element;
                        }
                    }
                } else if (unit.isConsistent()) {
                    // Bug 96313 - if there is no IJavaElement for the
                    // given offset, then check whether there are any
                    // children for this CU. There are if you've selected
                    // somewhere in the file and there aren't if there are
                    // compilation errors. Therefore, return one of these
                    // children and calculate the xrefs as though the user
                    // wants to display the xrefs for the entire file
                    IJavaElement elementAt = unit.getElementAt(offset);
                    if (elementAt != null) {
                        // a javaElement has been selected, therefore
                        // no need to go any further
                        return elementAt;
                    }
                    IResource res = unit.getCorrespondingResource();
                    if (res instanceof IFile) {
                        IFile file = (IFile) res;
                        IProject containingProject = file.getProject();
                        IMarker[] javaModelMarkers = containingProject.findMarkers(
                                IJavaModelMarker.JAVA_MODEL_PROBLEM_MARKER, false, IResource.DEPTH_INFINITE);
                        for (IMarker marker : javaModelMarkers) {
                            if (marker.getResource().equals(file)) {
                                // there is an error in the file, therefore
                                // we don't want to return any xrefs
                                return null;
                            }
                        }
                    }
                    // the selection was outside an IJavaElement, however, there
                    // are children for this compilation unit so we think you've
                    // selected outside of a java element.
                    if (elementAt == null && unit.getChildren().length != 0) {
                        return unit.getChildren()[0];
                    }
                }

            } catch (JavaModelException x) {
                if (!x.isDoesNotExist())
                    JavaPlugin.log(x.getStatus());
                // nothing found, be tolerant and go on
            } catch (CoreException e) {
            }
        }

        return null;
    }

    private synchronized Object getInternalLinkHelperService() {
        if (linkService == null) {
            try {
                try {
                    // e3.5: get helper from common navigator
                    Method method = CommonNavigator.class.getDeclaredMethod("getLinkHelperService"); //$NON-NLS-1$
                    method.setAccessible(true);
                    linkService = method.invoke(this);
                } catch (NoSuchMethodException e) {
                    // e3.3, e3.4: instantiate helper
                    Class<?> clazz = Class
                            .forName("org.eclipse.ui.internal.navigator.extensions.LinkHelperService"); //$NON-NLS-1$
                    Constructor<?> constructor = clazz.getConstructor(NavigatorContentService.class);
                    linkService = constructor
                            .newInstance((NavigatorContentService) getCommonViewer().getNavigatorContentService());
                }
                linkServiceMethod = linkService.getClass().getDeclaredMethod("getLinkHelpersFor", Object.class); //$NON-NLS-1$
            } catch (Throwable e) {
                SpringUIPlugin.log(e);
            }
        }
        return linkService;
    }

    private ILinkHelper[] getInternalLinkHelpersFor(Object object) {
        if (getInternalLinkHelperService() != null && linkServiceMethod != null) {
            try {
                return (ILinkHelper[]) linkServiceMethod.invoke(getInternalLinkHelperService(), object);
            } catch (IllegalArgumentException e) {
            } catch (IllegalAccessException e) {
            } catch (InvocationTargetException e) {
            }
        }
        return new ILinkHelper[0];
    }

    /**
     * Retrieves the element as represented by the given <code>selection</code>.
     */
    private Object getSelectedElement(IWorkbenchPart part, ISelection selection) {
        Object selectedElement = getSelectedJavaElement(part, selection);

        if (selectedElement == null) {
            selectedElement = getSelectedXmlElement(selection);

        }
        return selectedElement;
    }

    private IJavaElement getSelectedJavaElement(IWorkbenchPart part, ISelection selection) {
        if (selection instanceof IStructuredSelection) {
            IStructuredSelection structuredSelection = (IStructuredSelection) selection;
            Object first = structuredSelection.getFirstElement();
            if (first instanceof IJavaElement) {
                if (!(first instanceof IJavaProject)) {
                    return (IJavaElement) first;
                }
            }
        } else if (part instanceof IEditorPart && selection instanceof ITextSelection) {
            if (part instanceof JavaEditor) {
                JavaEditor je = (JavaEditor) part;
                ISourceReference sourceRef = computeHighlightRangeSourceReference(je);
                IJavaElement javaElement = (IJavaElement) sourceRef;
                return javaElement;
            }
        }
        return null;
    }

    private Object getSelectedXmlElement(ISelection selection) {
        Object selectedElement = null;
        if (selection instanceof IStructuredSelection) {
            IStructuredSelection structSelection = (IStructuredSelection) selection;
            Object obj = structSelection.getFirstElement();
            if (obj instanceof Element) {
                selectedElement = obj;
            } else if (obj instanceof Text) {
                Node parent = ((Text) obj).getParentNode();
                if (parent instanceof Element) {
                    selectedElement = parent;
                }
            } else if (obj instanceof Comment) {
                Node parent = ((Comment) obj).getParentNode();
                if (parent instanceof Element) {
                    selectedElement = parent;
                }
            }
        }
        return selectedElement;
    }

    private void selectReveal(TreeViewer viewer, Object element) {
        ILinkHelper[] helpers = getInternalLinkHelpersFor(element);
        for (ILinkHelper helper : helpers) {
            if (helper instanceof ILinkHelperExtension) {
                ISelection selection = ((ILinkHelperExtension) helper).findSelection(element);
                if (selection != null) {
                    viewer.getTree().setRedraw(false);
                    super.selectReveal(selection);
                    viewer.getTree().setRedraw(true);
                    break;
                }
            }
        }
    }

    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);
                }
            });
        }
    }

}