org.eclipse.recommenders.internal.calls.rcp.CallsApidocProvider.java Source code

Java tutorial

Introduction

Here is the source code for org.eclipse.recommenders.internal.calls.rcp.CallsApidocProvider.java

Source

/**
 * Copyright (c) 2011 Stefan Henss.
 * 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:
 *    Stefan Henss - initial API and implementation.
 *    Patrick Gottschaemmer, Olav Lenz - externalize Strings.
 */
package org.eclipse.recommenders.internal.calls.rcp;

import static com.google.common.collect.Iterables.isEmpty;
import static java.text.MessageFormat.format;
import static org.eclipse.recommenders.calls.ICallModel.DefinitionKind.*;
import static org.eclipse.recommenders.internal.apidocs.rcp.ApidocsViewUtils.*;
import static org.eclipse.recommenders.rcp.JavaElementSelectionEvent.JavaElementSelectionLocation.METHOD_BODY;
import static org.eclipse.recommenders.rcp.utils.JdtUtils.resolveMethod;
import static org.eclipse.recommenders.utils.Recommendations.*;
import static org.eclipse.swt.SWT.COLOR_INFO_FOREGROUND;

import java.util.Set;

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.IType;
import org.eclipse.jdt.core.JavaModelException;
import org.eclipse.jdt.core.dom.ASTNode;
import org.eclipse.jdt.core.dom.MethodDeclaration;
import org.eclipse.jface.layout.GridDataFactory;
import org.eclipse.recommenders.apidocs.rcp.ApidocProvider;
import org.eclipse.recommenders.apidocs.rcp.JavaSelectionSubscriber;
import org.eclipse.recommenders.calls.ICallModel;
import org.eclipse.recommenders.calls.ICallModel.DefinitionKind;
import org.eclipse.recommenders.calls.ICallModelProvider;
import org.eclipse.recommenders.internal.apidocs.rcp.ApidocsViewUtils;
import org.eclipse.recommenders.internal.calls.rcp.l10n.Messages;
import org.eclipse.recommenders.models.UniqueTypeName;
import org.eclipse.recommenders.models.rcp.IProjectCoordinateProvider;
import org.eclipse.recommenders.rcp.JavaElementResolver;
import org.eclipse.recommenders.rcp.JavaElementSelectionEvent;
import org.eclipse.recommenders.rcp.utils.JdtUtils;
import org.eclipse.recommenders.utils.Recommendation;
import org.eclipse.recommenders.utils.Recommendations;
import org.eclipse.recommenders.utils.names.IMethodName;
import org.eclipse.recommenders.utils.names.Names;
import org.eclipse.recommenders.utils.names.VmMethodName;
import org.eclipse.swt.SWT;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Label;

import com.google.common.annotations.Beta;
import com.google.common.base.Optional;
import com.google.common.collect.Sets;
import com.google.common.eventbus.EventBus;

@Beta
public final class CallsApidocProvider extends ApidocProvider {

    private final ICallModelProvider modelProvider;
    private final IProjectCoordinateProvider pcProvider;
    private final JavaElementResolver jdtResolver;
    private final EventBus workspaceBus;

    @Inject
    public CallsApidocProvider(ICallModelProvider modelProvider, IProjectCoordinateProvider pcProvider,
            JavaElementResolver jdtResolver, EventBus workspaceBus) {
        this.modelProvider = modelProvider;
        this.pcProvider = pcProvider;
        this.jdtResolver = jdtResolver;
        this.workspaceBus = workspaceBus;
    }

    private IType receiverType;
    private ICallModel model;
    private UniqueTypeName baseName;

    @JavaSelectionSubscriber
    public void onVariableSelection(final ILocalVariable var, final JavaElementSelectionEvent event,
            final Composite parent) {
        handle(var, var.getElementName(), var.getTypeSignature(), event, parent);
    }

    @JavaSelectionSubscriber(METHOD_BODY)
    public void onFieldSelection(final IField var, final JavaElementSelectionEvent event, final Composite parent)
            throws JavaModelException {
        handle(var, var.getElementName(), var.getTypeSignature(), event, parent);
    }

    private void handle(final IJavaElement variable, final String elementName, final String typeSignature,
            final JavaElementSelectionEvent event, final Composite parent) {
        final Optional<ASTNode> opt = event.getSelectedNode();
        if (!opt.isPresent()) {
            return;
        }

        final Optional<IType> varType = findVariableType(typeSignature, variable);
        if (!varType.isPresent()) {
            return;
        }

        receiverType = varType.get();
        baseName = pcProvider.toUniqueName(receiverType).orNull();
        if (baseName == null || !acquireModel()) {
            return;
        }
        try {
            final ASTNode node = opt.get();
            final Optional<MethodDeclaration> optAstMethod = findEnclosingMethod(node);
            final Optional<IMethod> optJdtMethod = resolveMethod(optAstMethod.orNull());
            if (!optJdtMethod.isPresent()) {
                return;
            }
            AstDefUseFinder defUse = new AstDefUseFinder(variable.getElementName(), optAstMethod.orNull());
            IMethod findFirstDeclaration = JdtUtils.findFirstDeclaration(optJdtMethod.get());
            IMethodName overrideContext = jdtResolver.toRecMethod(findFirstDeclaration).or(VmMethodName.NULL);
            Set<IMethodName> calls = Sets.newHashSet(defUse.getCalls());
            IMethodName definingMethod = defUse.getDefiningMethod().orNull();
            DefinitionKind kind = defUse.getDefinitionKind();
            // In the case of parameters we replace the defining method with the overridesContext
            if (PARAM == kind) {
                definingMethod = overrideContext;
            }

            if (kind == DefinitionKind.UNKNOWN) {
                // if the ast resolver could not find a definition of the
                // variable, it's a method return value? Ask
                // the context and try.
                if (definingMethod == null) {
                    kind = FIELD;
                } else if (definingMethod.isInit()) {
                    kind = DefinitionKind.NEW;
                } else {
                    kind = RETURN;
                }
            }

            model.setObservedOverrideContext(overrideContext);
            model.setObservedDefiningMethod(definingMethod);
            model.setObservedCalls(calls);
            model.setObservedDefinitionKind(kind);

            Iterable<Recommendation<IMethodName>> methodCalls = sortByRelevance(
                    Recommendations.filterRelevance(model.recommendCalls(), 0.01 / 100));
            runSyncInUiThread(new CallRecommendationsRenderer(overrideContext, methodCalls, calls,
                    variable.getElementName(), definingMethod, kind, parent));
        } finally {
            releaseModel();
        }
    }

    private Optional<IType> findVariableType(final String typeSignature, final IJavaElement parent) {
        return JdtUtils.findTypeFromSignature(typeSignature, parent);
    }

    private Optional<MethodDeclaration> findEnclosingMethod(final ASTNode node) {
        MethodDeclaration declaringNode = null;
        for (ASTNode p = node; p != null; p = p.getParent()) {
            if (p instanceof MethodDeclaration) {
                declaringNode = (MethodDeclaration) p;
                break;
            }
        }
        return Optional.fromNullable(declaringNode);
    }

    private boolean acquireModel() {
        model = modelProvider.acquireModel(baseName).orNull();
        return model != null;
    }

    private void releaseModel() {
        if (model != null) {
            modelProvider.releaseModel(model);
            model = null;
        }
    }

    private final class CallRecommendationsRenderer implements Runnable {

        private final IMethodName ctx;
        private final Iterable<Recommendation<IMethodName>> methodCalls;
        private final Set<IMethodName> calls;
        private final String varName;
        private final IMethodName def;
        private final DefinitionKind kind;
        private final Composite parent;

        private CallRecommendationsRenderer(IMethodName ctx, Iterable<Recommendation<IMethodName>> methodCalls,
                Set<IMethodName> calls, String varName, IMethodName def, final DefinitionKind kind,
                Composite parent) {
            this.ctx = ctx;
            this.methodCalls = methodCalls;
            this.calls = calls;
            this.varName = varName;
            this.def = def;
            this.kind = kind;
            this.parent = parent;
        }

        @Override
        public void run() {
            final Composite container = createComposite(parent, 4);
            final Label preamble2 = new Label(container, SWT.NONE);
            setInfoForegroundColor(preamble2);
            setInfoBackgroundColor(preamble2);
            preamble2.setLayoutData(GridDataFactory.swtDefaults().span(4, 1).indent(0, 0).create());
            if (isEmpty(methodCalls)) {
                preamble2.setText(
                        format(Messages.PROVIDER_INTRO_NO_RECOMMENDATIONS, receiverType.getElementName(), varName));
            } else {
                preamble2.setText(
                        format(Messages.PROVIDER_INTRO_RECOMMENDATIONS, receiverType.getElementName(), varName));
            }

            new Label(container, SWT.NONE).setLayoutData(
                    GridDataFactory.swtDefaults().span(4, 1).indent(0, 0).hint(SWT.DEFAULT, 1).create());
            for (final Recommendation<IMethodName> rec : methodCalls) {
                createLabel(container, percentageToRecommendationPhrase(asPercentage(rec)), true, false,
                        COLOR_INFO_FOREGROUND, false);

                createLabel(container, Messages.TABLE_CELL_RELATION_CALL + " ", false); //$NON-NLS-1$
                ApidocsViewUtils.createMethodLink(container, rec.getProposal(), jdtResolver, workspaceBus);
                double relevance = rec.getRelevance();
                String format = relevance < 0.01d ? Messages.TABLE_CELL_SUFFIX_PROMILLE
                        : Messages.TABLE_CELL_SUFFIX_PERCENTAGE;
                createLabel(container, " - " + format(format, relevance), false); //$NON-NLS-1$
            }

            new Label(container, SWT.SEPARATOR | SWT.HORIZONTAL);
            createLabel(container, "", false); //$NON-NLS-1$
            createLabel(container, "", false); //$NON-NLS-1$
            createLabel(container, "", false); //$NON-NLS-1$

            final Label preamble = new Label(container, SWT.NONE);
            preamble.setLayoutData(GridDataFactory.swtDefaults().span(4, 1).indent(0, 5).create());
            setInfoForegroundColor(preamble);
            setInfoBackgroundColor(preamble);
            final String text;
            if (ctx == VmMethodName.NULL) {
                text = format(Messages.PROVIDER_INFO_UNTRAINED_CONTEXT, receiverType.getElementName());
            } else {
                text = format(Messages.PROVIDER_INFO_LOCAL_VAR_CONTEXT, receiverType.getElementName(),
                        Names.vm2srcSimpleTypeName(ctx.getDeclaringType()) + "." + Names.vm2srcSimpleMethod(ctx)); //$NON-NLS-1$
            }
            preamble.setText(text);

            new Label(container, SWT.NONE).setLayoutData(
                    GridDataFactory.swtDefaults().span(4, 1).indent(0, 5).hint(SWT.DEFAULT, 1).create());

            if (def != null) {
                createLabel(container, Messages.TABLE_CELL_RELATION_DEFINED_BY, true, false, SWT.COLOR_DARK_GRAY,
                        false);
                createLabel(container, "", false, false, SWT.COLOR_DARK_GRAY, false); //$NON-NLS-1$
                if (def == VmMethodName.NULL) {
                    createLabel(container, Messages.TABLE_CELL_DEFINITION_UNTRAINED, false, false,
                            SWT.COLOR_DARK_GRAY, false);
                } else {
                    ApidocsViewUtils.createMethodLink(container, def, jdtResolver, workspaceBus);
                }
                createLabel(container, "- " + kind.toString().toLowerCase(), true, false, SWT.COLOR_DARK_GRAY, //$NON-NLS-1$
                        false);

            }

            for (final IMethodName observedCall : calls) {
                createLabel(container, Messages.TABLE_CELL_RELATION_OBSERVED, true, false, SWT.COLOR_DARK_GRAY,
                        false);
                createLabel(container, Messages.TABLE_CELL_RELATION_CALL + ' ', false, false, SWT.COLOR_DARK_GRAY,
                        false);
                ApidocsViewUtils.createMethodLink(container, observedCall, jdtResolver, workspaceBus);
                createLabel(container, "", true, false, SWT.COLOR_DARK_GRAY, false); //$NON-NLS-1$
            }
        }
    }
}