/*
* 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.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import groovyjarjarasm.asm.Opcodes;
import org.codehaus.groovy.ast.ClassHelper;
import org.codehaus.groovy.ast.ClassNode;
import org.codehaus.groovy.ast.FieldNode;
import org.codehaus.groovy.ast.MethodNode;
import org.codehaus.groovy.ast.ModuleNode;
import org.codehaus.groovy.eclipse.codeassist.ProposalUtils;
import org.eclipse.jdt.groovy.core.util.GroovyUtils;
import org.eclipse.jdt.groovy.search.AccessorSupport;
import org.eclipse.jdt.groovy.search.VariableScope;
public abstract class AbstractProposalCreator implements IProposalCreator {
/**
* The type of the LHS of the assignment statement associated with this
* invocation or null if there is none.
*/
protected ClassNode lhsType;
public void setLhsType(ClassNode lhsType) {
this.lhsType = lhsType;
}
protected VariableScope currentScope;
public void setCurrentScope(VariableScope currentScope) {
this.currentScope = currentScope;
}
protected Set<String> favoriteStaticMembers;
public void setFavoriteStaticMembers(Set<String> favoriteStaticMembers) {
this.favoriteStaticMembers = favoriteStaticMembers;
}
protected boolean checkName(String name) {
return name.charAt(0) != '<' && !name.contains("$");
}
/**
* Returns all fields, even those that are converted into properties.
*/
protected Collection<FieldNode> getAllFields(ClassNode thisType, Set<ClassNode> exclude) {
Map<String, FieldNode> allFields = new HashMap<String, FieldNode>();
// use a LinkedHashSet to preserve order
Set<ClassNode> types = new LinkedHashSet<ClassNode>();
getAllSupers(thisType, types, exclude);
for (ClassNode type : types) {
for (FieldNode field : type.getFields()) {
if (checkName(field.getName())) {
// only add new field if the new field is more accessible than the existing one
FieldNode existing = allFields.get(field.getName());
if (existing == null || leftIsMoreAccessible(field, existing)) {
allFields.put(field.getName(), field);
}
}
}
}
// don't do anything with these types next time
exclude.addAll(types);
return allFields.values();
}
protected List<MethodNode> getAllMethods(ClassNode type, Set<ClassNode> exclude) {
List<MethodNode> allMethods = type.getAllDeclaredMethods();
if (!exclude.isEmpty()) {
// remove all methods from classes that we have already visited
for (Iterator<MethodNode> methodIter = allMethods.iterator(); methodIter.hasNext();) {
if (exclude.contains(methodIter.next().getDeclaringClass())) {
methodIter.remove();
}
}
}
Set<ClassNode> types = new LinkedHashSet<ClassNode>();
getAllSupers(type, types, exclude);
// keep track of the already seen types so that next time, we won't include them
exclude.addAll(types);
return allMethods;
}
protected void getAllSupers(ClassNode type, Set<ClassNode> set, Set<ClassNode> exclude) {
if (type == null) {
return;
}
if (!exclude.contains(type)) {
set.add(type);
}
getAllSupers(type.getSuperClass(), set, exclude);
for (ClassNode inter : (Iterable<ClassNode>) type.getAllInterfaces()) {
if (!inter.getName().equals(type.getName())) {
getAllSupers(inter, set, exclude);
}
}
}
protected boolean isInterestingType(ClassNode type) {
return lhsType != null && GroovyUtils.isAssignable(type, lhsType);
}
/**
* find the most accessible element
*/
private static boolean leftIsMoreAccessible(FieldNode field, FieldNode existing) {
int leftAcc;
switch (field.getModifiers() & (Opcodes.ACC_PUBLIC | Opcodes.ACC_PRIVATE | Opcodes.ACC_PROTECTED)) {
case Opcodes.ACC_PUBLIC:
leftAcc = 0;
break;
case Opcodes.ACC_PROTECTED:
leftAcc = 1;
break;
case Opcodes.ACC_PRIVATE:
leftAcc = 3;
break;
default: // package default
leftAcc = 2;
break;
}
int rightAcc;
switch (existing.getModifiers() & (Opcodes.ACC_PUBLIC | Opcodes.ACC_PRIVATE | Opcodes.ACC_PROTECTED)) {
case Opcodes.ACC_PUBLIC:
rightAcc = 0;
break;
case Opcodes.ACC_PROTECTED:
rightAcc = 1;
break;
case Opcodes.ACC_PRIVATE:
rightAcc = 3;
break;
default: // package default
rightAcc = 2;
break;
}
return leftAcc < rightAcc;
}
protected void getAllSupersAsStrings(ClassNode type, Set<String> set) {
if (type == null) {
return;
}
set.add(type.getName());
getAllSupersAsStrings(type.getSuperClass(), set);
for (ClassNode inter : (Iterable<ClassNode>) type.getAllInterfaces()) {
if (! inter.getName().equals(type.getName())) {
getAllSupersAsStrings(inter, set);
}
}
}
/**
* Check to ensure that there is no field with a getter or setter name before creating the mock field.
*
* @param declaringClass declaring type of the method
* @param methodName method to check for
*/
protected boolean hasNoField(ClassNode declaringClass, String methodName) {
return declaringClass.getField(ProposalUtils.createMockFieldName(methodName)) == null &&
declaringClass.getField(ProposalUtils.createCapitalMockFieldName(methodName)) == null;
}
protected FieldNode createMockField(MethodNode method) {
FieldNode field = new FieldNode(ProposalUtils.createMockFieldName(
method.getName()), method.getModifiers(), method.getReturnType(), method.getDeclaringClass(), null);
field.setDeclaringClass(method.getDeclaringClass());
field.setSourcePosition(method);
return field;
}
/**
* Determine the kind of accessor the prefix corresponds to, if any
*/
protected AccessorSupport findLooselyMatchedAccessorKind(String prefix, String methodName, boolean isCategory) {
AccessorSupport accessor = AccessorSupport.create(methodName, isCategory);
if (accessor.isAccessor()) {
String newName = ProposalUtils.createMockFieldName(methodName);
return ProposalUtils.looselyMatches(prefix, newName) ? accessor : AccessorSupport.NONE;
} else {
return AccessorSupport.NONE;
}
}
public boolean redoForLoopClosure() {
return true;
}
protected static ClassNode tryResolveClassNode(String typeName, ModuleNode module) {
for (ClassNode t : module.getClasses()) {
if (t.getName().equals(typeName)) {
return t;
}
}
try {
//ClassNode type = ((EclipseSourceUnit) module.getContext()).resolver.resolve(typeName);
Class<?> t = module.getContext().getClassLoader().loadClass(typeName, true, true, true);
return ClassHelper.make(t);
} catch (ClassNotFoundException e) {
return null;
}
}
}