/** * 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.calls.rcp; import static org.apache.commons.lang3.StringUtils.isEmpty; import static org.eclipse.recommenders.calls.ICallModel.DefinitionKind.*; import static org.eclipse.recommenders.completion.rcp.CompletionContextKey.RECEIVER_NAME; import static org.eclipse.recommenders.internal.calls.rcp.l10n.LogMessages.ERROR_RECEIVER_TYPE_LOOKUP_FAILED; import static org.eclipse.recommenders.utils.Logs.log; import java.util.List; import java.util.Map; import org.eclipse.jdt.core.IMethod; import org.eclipse.jdt.core.IType; import org.eclipse.jdt.core.ITypeHierarchy; import org.eclipse.jdt.core.dom.MethodDeclaration; import org.eclipse.jdt.internal.codeassist.complete.CompletionOnQualifiedNameReference; import org.eclipse.jdt.internal.compiler.ast.ASTNode; import org.eclipse.jdt.internal.compiler.lookup.ProblemBinding; import org.eclipse.jdt.internal.corext.util.JdtFlags; import org.eclipse.jdt.internal.corext.util.SuperTypeHierarchyCache; import org.eclipse.recommenders.calls.ICallModel; import org.eclipse.recommenders.calls.ICallModel.DefinitionKind; import org.eclipse.recommenders.completion.rcp.CompletionContextKey; import org.eclipse.recommenders.completion.rcp.ICompletionContextFunction; import org.eclipse.recommenders.completion.rcp.IRecommendersCompletionContext; import org.eclipse.recommenders.rcp.utils.JdtUtils; import org.eclipse.recommenders.utils.names.IMethodName; @SuppressWarnings({ "rawtypes", "restriction" }) public final class CallCompletionContextFunctions { private CallCompletionContextFunctions() { // Not meant to be instantiated } // TODO need to rename public static final CompletionContextKey<IType> RECEIVER_TYPE2 = new CompletionContextKey<IType>(); public static final CompletionContextKey<IMethodName> RECEIVER_DEF_BY = new CompletionContextKey<IMethodName>(); public static final CompletionContextKey<List<IMethodName>> RECEIVER_CALLS = new CompletionContextKey<List<IMethodName>>(); public static final CompletionContextKey<DefinitionKind> RECEIVER_DEF_TYPE = new CompletionContextKey<ICallModel.DefinitionKind>(); public static Map<CompletionContextKey, ICompletionContextFunction> registerDefaults( Map<CompletionContextKey, ICompletionContextFunction> functions) { ReceiverCallsCompletionContextFunction f = new ReceiverCallsCompletionContextFunction(); functions.put(RECEIVER_CALLS, f); functions.put(RECEIVER_DEF_BY, f); functions.put(RECEIVER_DEF_TYPE, f); functions.put(RECEIVER_TYPE2, new ReceiverTypeContextFunction()); return functions; } public static class ReceiverCallsCompletionContextFunction implements ICompletionContextFunction { @SuppressWarnings("unchecked") @Override public Object compute(IRecommendersCompletionContext context, CompletionContextKey key) { List<IMethodName> calls = null; DefinitionKind defType = null; IMethodName defBy = null; MethodDeclaration method = context.get(CompletionContextKey.ENCLOSING_AST_METHOD, null); if (method != null) { String receiverName = context.get(RECEIVER_NAME, null); AstDefUseFinder f = new AstDefUseFinder(receiverName, method); calls = f.getCalls(); defType = f.getDefinitionKind(); defBy = f.getDefiningMethod().orNull(); if (defType == 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. IMethodName def = context.getMethodDef().orNull(); if (def == null) { if (receiverName.isEmpty()) { defType = THIS; } else { defType = FIELD; } } else if (def.isInit()) { defType = DefinitionKind.NEW; defBy = def; } else { defType = RETURN; defBy = def; } } } context.set(RECEIVER_CALLS, calls); context.set(RECEIVER_DEF_TYPE, defType); context.set(RECEIVER_DEF_BY, defBy); return context.get(key).orNull(); } } public static class ReceiverTypeContextFunction implements ICompletionContextFunction<Object> { @Override public Object compute(IRecommendersCompletionContext context, CompletionContextKey<Object> key) { IType receiverType = null; try { receiverType = findReceiver(context); } catch (Exception e) { log(ERROR_RECEIVER_TYPE_LOOKUP_FAILED, e); } context.set(RECEIVER_TYPE2, receiverType); return receiverType; } private IType findReceiver(IRecommendersCompletionContext context) throws Exception { ASTNode completionNode = context.getCompletionNode().orNull(); IType receiverType = context.getReceiverType().orNull(); String receiverName = context.getReceiverName(); if (isCompletionOnUnresolvedTypeName(completionNode, receiverType, receiverName)) { return null; } if (isExplicitThis(receiverName) || isImplicitThis(receiverType, receiverName)) { final IMethod m = context.getEnclosingMethod().orNull(); if (m == null || JdtFlags.isStatic(m)) { return receiverType; } final IType type = m.getDeclaringType(); final ITypeHierarchy hierarchy = SuperTypeHierarchyCache.getTypeHierarchy(type); receiverType = hierarchy.getSuperclass(type); // XXX workaround for https://bugs.eclipse.org/bugs/show_bug.cgi?id=441021 if (receiverType == null) { String superclassTypeSignature = type.getSuperclassTypeSignature(); if (superclassTypeSignature != null) { receiverType = JdtUtils.findTypeFromSignature(superclassTypeSignature, type).orNull(); } } } return receiverType; } private boolean isCompletionOnUnresolvedTypeName(ASTNode completionNode, IType receiverType, String receiverName) { if (!(completionNode instanceof CompletionOnQualifiedNameReference)) { return false; } CompletionOnQualifiedNameReference ref = (CompletionOnQualifiedNameReference) completionNode; return ref.binding instanceof ProblemBinding && receiverType == null && isEmpty(receiverName); } private boolean isImplicitThis(IType receiverType, String receiverName) { return receiverType == null && isEmpty(receiverName); } private boolean isExplicitThis(String receiverName) { return "this".equals(receiverName); //$NON-NLS-1$ } } }