org.eclipse.recommenders.internal.apidocs.rcp.StaticHooksProvider.java Source code

Java tutorial

Introduction

Here is the source code for org.eclipse.recommenders.internal.apidocs.rcp.StaticHooksProvider.java

Source

/**
 * Copyright (c) 2010, 2014 Darmstadt University of Technology.
 * 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:
 *    Marcel Bruch - initial API and implementation.
 *    Olav Lenz - externalize Strings.
 */
package org.eclipse.recommenders.internal.apidocs.rcp;

import static com.google.common.base.Optional.*;
import static org.eclipse.jdt.ui.JavaElementLabels.*;
import static org.eclipse.recommenders.internal.apidocs.rcp.ApidocsViewUtils.*;
import static org.eclipse.recommenders.internal.apidocs.rcp.l10n.LogMessages.ERROR_FAILED_TO_DETERMINE_STATIC_MEMBERS;
import static org.eclipse.recommenders.rcp.JavaElementSelectionEvent.JavaElementSelectionLocation.METHOD_DECLARATION;
import static org.eclipse.recommenders.utils.Logs.log;

import java.util.Comparator;
import java.util.List;
import java.util.concurrent.ExecutionException;

import javax.inject.Inject;

import org.eclipse.jdt.core.IField;
import org.eclipse.jdt.core.IJavaElement;
import org.eclipse.jdt.core.ILocalVariable;
import org.eclipse.jdt.core.IMethod;
import org.eclipse.jdt.core.IPackageFragment;
import org.eclipse.jdt.core.IPackageFragmentRoot;
import org.eclipse.jdt.core.IType;
import org.eclipse.jdt.core.ITypeRoot;
import org.eclipse.jdt.core.JavaModelException;
import org.eclipse.jdt.internal.corext.util.JdtFlags;
import org.eclipse.jdt.ui.JavaElementLabels;
import org.eclipse.jface.resource.JFaceResources;
import org.eclipse.recommenders.apidocs.rcp.ApidocProvider;
import org.eclipse.recommenders.apidocs.rcp.JavaSelectionSubscriber;
import org.eclipse.recommenders.internal.apidocs.rcp.l10n.Messages;
import org.eclipse.recommenders.rcp.JavaElementSelectionEvent;
import org.eclipse.recommenders.rcp.utils.JdtUtils;
import org.eclipse.recommenders.utils.IOUtils;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.StyleRange;
import org.eclipse.swt.custom.StyledText;
import org.eclipse.swt.events.MouseEvent;
import org.eclipse.swt.events.MouseMoveListener;
import org.eclipse.swt.graphics.Cursor;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.Listener;

import com.google.common.base.Optional;
import com.google.common.collect.Lists;
import com.google.common.collect.TreeMultimap;
import com.google.common.eventbus.EventBus;

@SuppressWarnings("restriction")
public class StaticHooksProvider extends ApidocProvider {

    private final class HooksRendererRunnable implements Runnable {
        private final TreeMultimap<IType, IMethod> index;
        private final Composite parent;
        private StyledText styledText;

        private HooksRendererRunnable(final TreeMultimap<IType, IMethod> index, final Composite parent) {
            this.index = index;
            this.parent = parent;
        }

        @Override
        public void run() {
            final Composite container = createComposite(parent, 1);
            if (index.isEmpty()) {
                createLabel(container, Messages.PROVIDER_INTRO_NO_STATIC_HOOKS_FOUND, true);
            }

            final List<StyleRange> typeRanges = Lists.newLinkedList();
            final StringBuilder sb = new StringBuilder();
            for (final IType type : index.keySet()) {
                final String typeLabel = type.getFullyQualifiedName();
                final int typeLabelBegin = sb.length();
                sb.append(typeLabel);
                final int typeLabelEnd = sb.length();
                final StyleRange styleRange = new StyleRange();
                styleRange.rise = -12;
                styleRange.start = typeLabelBegin;
                styleRange.length = typeLabelEnd - typeLabelBegin;
                styleRange.fontStyle = SWT.BOLD;
                styleRange.data = type;
                styleRange.font = JFaceResources.getHeaderFont();
                typeRanges.add(styleRange);
                sb.append(IOUtils.LINE_SEPARATOR);
                for (final IMethod method : index.get(type)) {
                    sb.append("    "); //$NON-NLS-1$
                    final int methodLabelBegin = sb.length();
                    final String methodLabel = getElementLabel(method, M_APP_RETURNTYPE | M_PARAMETER_TYPES);
                    sb.append(methodLabel);
                    final int methodLabelEnd = sb.length();
                    final StyleRange methodStyleRange = new StyleRange();
                    methodStyleRange.start = methodLabelBegin;
                    methodStyleRange.length = methodLabelEnd - methodLabelBegin;
                    methodStyleRange.data = method;
                    methodStyleRange.underline = true;
                    methodStyleRange.font = JFaceResources.getDialogFont();
                    methodStyleRange.foreground = Display.getDefault().getSystemColor(SWT.COLOR_BLUE);
                    typeRanges.add(methodStyleRange);
                    sb.append(IOUtils.LINE_SEPARATOR);
                }
            }

            styledText = new StyledText(container, SWT.NONE);
            styledText.setRedraw(false);
            styledText.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
            setInfoBackgroundColor(styledText);
            setInfoForegroundColor(styledText);
            styledText.setEditable(false);
            styledText.setText(sb.toString());
            styledText.setStyleRanges(typeRanges.toArray(new StyleRange[0]));
            final Cursor c1 = Display.getDefault().getSystemCursor(SWT.CURSOR_ARROW);
            final Cursor c2 = Display.getDefault().getSystemCursor(SWT.CURSOR_HAND);
            styledText.addListener(SWT.MouseDown, new Listener() {

                @Override
                public void handleEvent(final Event event) {
                    // It is up to the application to determine when and how a link should be activated.
                    // In this snippet links are activated on mouse down when the control key is held down
                    final Optional<IMethod> opt = getSelectedMethod(event.x, event.y);
                    if (opt.isPresent()) {
                        final JavaElementSelectionEvent sEvent = new JavaElementSelectionEvent(opt.get(),
                                METHOD_DECLARATION);
                        workspaceBus.post(sEvent);
                    }
                }
            });

            styledText.addMouseMoveListener(new MouseMoveListener() {

                @Override
                public void mouseMove(final MouseEvent e) {
                    final Optional<IMethod> opt = getSelectedMethod(e.x, e.y);
                    if (opt.isPresent()) {
                        styledText.setCursor(c2);
                    } else {
                        styledText.setCursor(c1);
                    }

                }
            });
            styledText.setRedraw(true);
        }

        private Optional<StyleRange> getSelectedStyleRange(final int x, final int y) {
            try {
                final int offset = styledText.getOffsetAtLocation(new Point(x, y));
                final StyleRange style = styledText.getStyleRangeAtOffset(offset);
                return Optional.fromNullable(style);
            } catch (final IllegalArgumentException e) {
                return absent();
            }
        }

        private Optional<IMethod> getSelectedMethod(final int x, final int y) {
            final Optional<StyleRange> range = getSelectedStyleRange(x, y);
            if (!range.isPresent()) {
                return absent();
            }

            final Object data = range.get().data;
            if (data instanceof IMethod) {
                return of((IMethod) data);
            } else {
                return absent();
            }
        }
    }

    private static class MethodNameComparator implements Comparator<IMethod> {

        @Override
        public int compare(final IMethod o1, final IMethod o2) {
            final String s1 = JavaElementLabels.getElementLabel(o1, JavaElementLabels.ALL_FULLY_QUALIFIED);
            final String s2 = JavaElementLabels.getElementLabel(o2, JavaElementLabels.ALL_FULLY_QUALIFIED);
            return s1.compareTo(s2);
        }
    }

    private static class TypeNameComparator implements Comparator<IType> {

        @Override
        public int compare(final IType arg0, final IType arg1) {
            final String s0 = arg0.getFullyQualifiedName();
            final String s1 = arg1.getFullyQualifiedName();
            return s0.compareTo(s1);
        }
    }

    private final EventBus workspaceBus;

    @Inject
    public StaticHooksProvider(final EventBus workspaceBus) {
        this.workspaceBus = workspaceBus;

    }

    @JavaSelectionSubscriber
    public void onPackageRootSelection(final IPackageFragmentRoot root, final JavaElementSelectionEvent event,
            final Composite parent) throws ExecutionException {

        final TreeMultimap<IType, IMethod> index = TreeMultimap.create(new TypeNameComparator(),
                new MethodNameComparator());
        try {
            for (final IJavaElement e : root.getChildren()) {
                if (e.getElementType() == IJavaElement.PACKAGE_FRAGMENT) {
                    final IPackageFragment pkg = (IPackageFragment) e;
                    findStaticHooks(pkg, index);
                    pkg.close();
                }
            }
        } catch (final Exception x) {
            log(ERROR_FAILED_TO_DETERMINE_STATIC_MEMBERS, x, root.getElementName());
        }
        runSyncInUiThread(new HooksRendererRunnable(index, parent));
    }

    @JavaSelectionSubscriber
    public void onPackageSelection(final IPackageFragment pkg, final JavaElementSelectionEvent event,
            final Composite parent) throws ExecutionException {

        final TreeMultimap<IType, IMethod> index = TreeMultimap.create(new TypeNameComparator(),
                new MethodNameComparator());
        try {
            findStaticHooks(pkg, index);
        } catch (final Exception e) {
            log(ERROR_FAILED_TO_DETERMINE_STATIC_MEMBERS, e, pkg.getElementName());
        }

        runSyncInUiThread(new HooksRendererRunnable(index, parent));
    }

    @JavaSelectionSubscriber
    public void onVariableSelection(ILocalVariable var, JavaElementSelectionEvent event, Composite parent)
            throws ExecutionException {
        IType type = ApidocsViewUtils.findType(var).orNull();
        if (type != null) {
            onPackageSelection(type.getPackageFragment(), event, parent);
        }
    }

    @JavaSelectionSubscriber
    public void onVariableSelection(IField var, JavaElementSelectionEvent event, Composite parent)
            throws ExecutionException, JavaModelException {
        IType type = ApidocsViewUtils.findType(var).orNull();
        if (type != null) {
            onPackageSelection(type.getPackageFragment(), event, parent);
        }
    }

    @JavaSelectionSubscriber
    public void onJavaElementSelection(final IJavaElement e, final JavaElementSelectionEvent event,
            final Composite parent) throws ExecutionException {
        IPackageFragment pkg = (IPackageFragment) e.getAncestor(IJavaElement.PACKAGE_FRAGMENT);
        if (pkg != null) {
            onPackageSelection(pkg, event, parent);
        }
    }

    private void findStaticHooks(final IPackageFragment pkg, final TreeMultimap<IType, IMethod> index)
            throws JavaModelException {
        for (final ITypeRoot f : pkg.getClassFiles()) {
            findStaticHooks(index, f);
        }
        for (final ITypeRoot f : pkg.getCompilationUnits()) {
            findStaticHooks(index, f);
        }
    }

    private void findStaticHooks(final TreeMultimap<IType, IMethod> index, final ITypeRoot root)
            throws JavaModelException {
        final IType type = root.findPrimaryType();
        if (type == null) {
            return;
        }
        if (!type.isClass()) {
            return;
        }

        for (final IMethod m : type.getMethods()) {
            if (JdtFlags.isStatic(m) && JdtFlags.isPublic(m) && !JdtUtils.isInitializer(m)) {
                index.put(type, m);
            }
        }
    }
}