/*
* Copyright 2009-2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.codehaus.groovy.eclipse.codeassist.creators;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.codehaus.groovy.ast.ClassNode;
import org.codehaus.groovy.ast.ImportNode;
import org.codehaus.groovy.ast.MethodNode;
import org.codehaus.groovy.ast.ModuleNode;
import org.codehaus.groovy.eclipse.GroovyLogManager;
import org.codehaus.groovy.eclipse.TraceCategory;
import org.codehaus.groovy.eclipse.codeassist.ProposalUtils;
import org.codehaus.groovy.eclipse.codeassist.proposals.GroovyFieldProposal;
import org.codehaus.groovy.eclipse.codeassist.proposals.GroovyMethodProposal;
import org.codehaus.groovy.eclipse.codeassist.proposals.IGroovyProposal;
import org.eclipse.jdt.core.compiler.CharOperation;
import org.eclipse.jdt.groovy.search.AccessorSupport;
import org.eclipse.jdt.groovy.search.VariableScope;
/**
* Generates all of the method proposals for a given location.
* Also will add the property form of accessor methods if appropriate.
*/
public class MethodProposalCreator extends AbstractProposalCreator {
private Set<ClassNode> alreadySeen = new HashSet<ClassNode>();
public List<IGroovyProposal> findAllProposals(ClassNode type, Set<ClassNode> categories, String prefix, boolean isStatic, boolean isPrimary) {
List<IGroovyProposal> proposals = new ArrayList<IGroovyProposal>();
boolean firstTime = alreadySeen.isEmpty();
List<MethodNode> allMethods = getAllMethods(type, alreadySeen);
Set<String> alreadySeenFields = new HashSet<String>();
if (isStatic) {
// "class" is added by FieldProposalCreator
alreadySeenFields.add("class");
}
for (MethodNode method : allMethods) {
String methodName = method.getName();
if ((!isStatic || method.isStatic() || method.getDeclaringClass() == VariableScope.OBJECT_CLASS_NODE) && checkName(methodName)) {
if (ProposalUtils.looselyMatches(prefix, methodName)) {
GroovyMethodProposal proposal = new GroovyMethodProposal(method);
setRelevanceMultiplier(proposal, firstTime, isStatic);
proposals.add(proposal);
}
AccessorSupport accessor = findLooselyMatchedAccessorKind(prefix, methodName, false);
if (accessor.isAccessorKind(method, false)) {
// if there is a getter or setter, then add a field proposal as well
String mockFieldName = ProposalUtils.createMockFieldName(methodName);
if (!alreadySeenFields.contains(mockFieldName)) {
// be careful not to add fields twice
alreadySeenFields.add(mockFieldName);
if (hasNoField(method.getDeclaringClass(), methodName)) {
GroovyFieldProposal proposal = new GroovyFieldProposal(createMockField(method));
proposal.setRelevanceMultiplier(/*isInterestingType ? 11 :*/ 1);
proposals.add(proposal);
}
}
}
}
}
if (currentScope != null) {
ClassNode enclosingTypeDeclaration = currentScope.getEnclosingTypeDeclaration();
if (enclosingTypeDeclaration != null && firstTime && isPrimary && type.getModule() != null) {
findStaticImportProposals(proposals, prefix, type.getModule());
findStaticFavoriteProposals(proposals, prefix, type.getModule());
}
}
return proposals;
}
private void findStaticImportProposals(List<IGroovyProposal> proposals, String prefix, ModuleNode module) {
for (Map.Entry<String, ImportNode> entry : module.getStaticImports().entrySet()) {
String fieldName = entry.getValue().getFieldName();
if (ProposalUtils.looselyMatches(prefix, fieldName)) {
List<MethodNode> methods = entry.getValue().getType().getDeclaredMethods(fieldName);
if (methods != null) {
for (MethodNode method : methods) {
if (method.isStatic()) {
addIfNotPresent(proposals, new GroovyMethodProposal(method));
}
}
}
}
}
for (Map.Entry<String, ImportNode> entry : module.getStaticStarImports().entrySet()) {
ClassNode type = entry.getValue().getType();
if (type != null) {
for (MethodNode method : (Iterable<MethodNode>) type.getMethods()) {
if (method.isStatic() && ProposalUtils.looselyMatches(prefix, method.getName())) {
addIfNotPresent(proposals, new GroovyMethodProposal(method));
}
}
}
}
}
private void findStaticFavoriteProposals(List<IGroovyProposal> proposals, String prefix, ModuleNode module) {
for (String favoriteStaticMember : favoriteStaticMembers) {
int pos = favoriteStaticMember.lastIndexOf('.');
String typeName = favoriteStaticMember.substring(0, pos);
String fieldName = favoriteStaticMember.substring(pos + 1);
ClassNode typeNode = tryResolveClassNode(typeName, module);
if (typeNode == null) {
if (GroovyLogManager.manager.hasLoggers()) {
GroovyLogManager.manager.log(TraceCategory.CONTENT_ASSIST, "Cannot resolve favorite type " + typeName);
}
continue;
}
if ("*".equals(fieldName)) {
for (MethodNode method : (Iterable<MethodNode>) typeNode.getMethods()) {
if (method.isStatic() && ProposalUtils.looselyMatches(prefix, method.getName())) {
GroovyMethodProposal proposal = new GroovyMethodProposal(method);
proposal.setRequiredStaticImport(typeName + '.' + method.getName());
addIfNotPresent(proposals, proposal);
}
}
} else {
if (ProposalUtils.looselyMatches(prefix, fieldName)) {
List<MethodNode> methods = typeNode.getDeclaredMethods(fieldName);
for (MethodNode method : methods) {
if (method.isStatic()) {
GroovyMethodProposal proposal = new GroovyMethodProposal(method);
proposal.setRequiredStaticImport(favoriteStaticMember);
addIfNotPresent(proposals, proposal);
}
}
}
}
}
}
private void setRelevanceMultiplier(GroovyMethodProposal proposal, boolean firstTime, boolean isStatic) {
MethodNode method = proposal.getMethod();
float relevanceMultiplier;
if (isStatic && method.isStatic()) {
relevanceMultiplier = 10.0f;
} else if (!method.isStatic()) {
relevanceMultiplier = 1.00f;
} else {
relevanceMultiplier = 0.77f;
}
// de-emphasize 'this' references inside closure
if (!firstTime) {
relevanceMultiplier *= 0.1f;
}
if (isInterestingType(method.getReturnType())) {
relevanceMultiplier *= 10.01f; // must beat out uninteresting-typed fields, which have a very high relevance
}
proposal.setRelevanceMultiplier(relevanceMultiplier);
}
private static void addIfNotPresent(List<IGroovyProposal> proposals, GroovyMethodProposal proposal) {
if (!proposals.isEmpty()) {
char[] sig = ProposalUtils.createMethodSignature(proposal.getMethod());
for (IGroovyProposal igp : proposals) {
if (igp instanceof GroovyMethodProposal) {
GroovyMethodProposal gmp = (GroovyMethodProposal) igp;
if (gmp.getMethod().isStatic() && CharOperation.equals(sig,
ProposalUtils.createMethodSignature(gmp.getMethod()))) {
return;
}
}
}
}
proposals.add(proposal);
}
}