/** * Copyright (c) 2010, 2013 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. */ package org.eclipse.recommenders.internal.overrides.rcp; import static java.lang.String.valueOf; import static java.text.MessageFormat.format; import static org.eclipse.recommenders.completion.rcp.processable.ProposalTag.RECOMMENDERS_SCORE; import static org.eclipse.recommenders.rcp.SharedImages.Images.OVR_STAR; import static org.eclipse.recommenders.utils.Recommendations.asPercentage; import java.util.List; import javax.inject.Inject; import javax.inject.Provider; import org.eclipse.jdt.core.CompletionProposal; import org.eclipse.jdt.core.IMethod; import org.eclipse.jdt.core.IType; import org.eclipse.jdt.core.JavaModelException; import org.eclipse.jdt.internal.codeassist.complete.CompletionOnFieldType; import org.eclipse.jdt.internal.compiler.ast.ASTNode; import org.eclipse.jface.viewers.IDecoration; import org.eclipse.recommenders.completion.rcp.IRecommendersCompletionContext; import org.eclipse.recommenders.completion.rcp.processable.IProcessableProposal; import org.eclipse.recommenders.completion.rcp.processable.OverlayImageProposalProcessor; import org.eclipse.recommenders.completion.rcp.processable.ProposalProcessorManager; import org.eclipse.recommenders.completion.rcp.processable.SessionProcessor; import org.eclipse.recommenders.completion.rcp.processable.SimpleProposalProcessor; import org.eclipse.recommenders.coordinates.ProjectCoordinate; import org.eclipse.recommenders.internal.overrides.rcp.l10n.LogMessages; import org.eclipse.recommenders.internal.overrides.rcp.l10n.Messages; import org.eclipse.recommenders.models.UniqueTypeName; import org.eclipse.recommenders.models.rcp.IProjectCoordinateProvider; import org.eclipse.recommenders.overrides.IOverrideModel; import org.eclipse.recommenders.overrides.IOverrideModelProvider; import org.eclipse.recommenders.rcp.JavaElementResolver; import org.eclipse.recommenders.rcp.SharedImages; import org.eclipse.recommenders.rcp.utils.JdtUtils; import org.eclipse.recommenders.utils.Logs; 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.VmMethodName; @SuppressWarnings({ "restriction" }) public class OverrideCompletionSessionProcessor extends SessionProcessor { private final Provider<IProjectCoordinateProvider> pcProvider; private final Provider<IOverrideModelProvider> modelProvider; private final JavaElementResolver jdtCache; private final OverridesRcpPreferences prefs; private final OverlayImageProposalProcessor overlayProcessor; private IRecommendersCompletionContext ctx; private IType enclosingType; private IType supertype; private ProjectCoordinate pc; private IOverrideModel model; private List<Recommendation<IMethodName>> recommendations; @Inject public OverrideCompletionSessionProcessor(Provider<IProjectCoordinateProvider> pcProvider, Provider<IOverrideModelProvider> modelProvider, JavaElementResolver cache, SharedImages images, OverridesRcpPreferences prefs) { this.pcProvider = pcProvider; this.modelProvider = modelProvider; this.jdtCache = cache; this.prefs = prefs; this.overlayProcessor = new OverlayImageProposalProcessor(images.getDescriptor(OVR_STAR), IDecoration.TOP_LEFT); }; @Override public boolean startSession(IRecommendersCompletionContext context) { recommendations = null; ctx = context; if (isSupportedCompletionType() && findEnclosingType() && findSuperclass() && findProjectCoordinate() && hasModel()) { try { computeRecommendations(); return true; } catch (Exception e) { Logs.log(LogMessages.ERROR_FAILED_TO_COMPUTE_OVERRIDE_RECOMMENDATIONS, e); } finally { releaseModel(); } } return false; } private boolean isSupportedCompletionType() { ASTNode completionNode = ctx.getCompletionNode().orNull(); return completionNode != null && completionNode.getClass() == CompletionOnFieldType.class; } private boolean findEnclosingType() { enclosingType = ctx.getEnclosingType().orNull(); return enclosingType != null; } private boolean findSuperclass() { supertype = JdtUtils.findSuperclass(enclosingType).orNull(); return supertype != null; } private boolean findProjectCoordinate() { pc = pcProvider.get().resolve(supertype).orNull(); return pc != null; } private boolean hasModel() { UniqueTypeName name = new UniqueTypeName(pc, jdtCache.toRecType(supertype)); model = modelProvider.get().acquireModel(name).orNull(); return model != null; } private void releaseModel() { if (model != null) { modelProvider.get().releaseModel(model); } } private void computeRecommendations() throws JavaModelException { for (final IMethod m : enclosingType.getMethods()) { final IMethod superMethod = JdtUtils.findOverriddenMethod(m).orNull(); if (superMethod != null) { final IMethodName recSuperMethod = jdtCache.toRecMethod(superMethod).or(VmMethodName.NULL); model.setObservedMethod(recSuperMethod); } } recommendations = Recommendations.top(model.recommendOverrides(), prefs.maxNumberOfProposals, prefs.minProposalPercentage / 100d); } @Override public void process(IProcessableProposal proposal) throws Exception { if (recommendations == null || recommendations.isEmpty()) { return; } CompletionProposal coreProposal = proposal.getCoreProposal().orNull(); if (coreProposal == null) { return; } final String prefix = ctx.getPrefix(); switch (coreProposal.getKind()) { case CompletionProposal.METHOD_DECLARATION: final String signature = valueOf(coreProposal.getSignature()).replace('.', '/'); final String name = valueOf(coreProposal.getName()); final String propSignature = (name + signature).replaceAll("<\\.>", ""); //$NON-NLS-1$ //$NON-NLS-2$ for (final Recommendation<IMethodName> r : recommendations) { IMethodName rMethod = r.getProposal(); if (!rMethod.getName().startsWith(prefix)) { continue; } final String recSignature = rMethod.getSignature(); if (!recSignature.equals(propSignature)) { continue; } // XXX rather high value but otherwise the default constructor shows up between the overrides // proposals final int boost = prefs.changeProposalRelevance ? 1000 + asPercentage(r) : 0; final String label = prefs.decorateProposalText ? format(Messages.PROPOSAL_LABEL_PERCENTAGE, r.getRelevance()) : ""; //$NON-NLS-1$ if (boost > 0) { // TODO Shouldn't this convey the real boost? proposal.setTag(RECOMMENDERS_SCORE, asPercentage(r)); } ProposalProcessorManager mgr = proposal.getProposalProcessorManager(); mgr.addProcessor(new SimpleProposalProcessor(boost, label)); if (prefs.decorateProposalIcon) { mgr.addProcessor(overlayProcessor); } return; } } } }