blob: f9001e94e166b2161a45b5a6f7df38d3a220e953 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2000, 2017 IBM Corporation and others.
* 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:
* IBM Corporation - initial API and implementation
* Benjamin Muskalla - [quick fix] Create Method in void context should 'box' void. - https://bugs.eclipse.org/bugs/show_bug.cgi?id=107985
*******************************************************************************/
package org.eclipse.jdt.internal.ui.text.correction.proposals;
import java.util.List;
import org.eclipse.swt.graphics.Image;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.jdt.core.ICompilationUnit;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.NamingConventions;
import org.eclipse.jdt.core.dom.AST;
import org.eclipse.jdt.core.dom.ASTNode;
import org.eclipse.jdt.core.dom.AnonymousClassDeclaration;
import org.eclipse.jdt.core.dom.BodyDeclaration;
import org.eclipse.jdt.core.dom.ClassInstanceCreation;
import org.eclipse.jdt.core.dom.Expression;
import org.eclipse.jdt.core.dom.ExpressionStatement;
import org.eclipse.jdt.core.dom.IBinding;
import org.eclipse.jdt.core.dom.IExtendedModifier;
import org.eclipse.jdt.core.dom.IMethodBinding;
import org.eclipse.jdt.core.dom.ITypeBinding;
import org.eclipse.jdt.core.dom.MethodDeclaration;
import org.eclipse.jdt.core.dom.MethodInvocation;
import org.eclipse.jdt.core.dom.Modifier;
import org.eclipse.jdt.core.dom.Name;
import org.eclipse.jdt.core.dom.ParameterizedType;
import org.eclipse.jdt.core.dom.PrimitiveType;
import org.eclipse.jdt.core.dom.SimpleName;
import org.eclipse.jdt.core.dom.SingleVariableDeclaration;
import org.eclipse.jdt.core.dom.SuperMethodInvocation;
import org.eclipse.jdt.core.dom.Type;
import org.eclipse.jdt.core.dom.TypeDeclaration;
import org.eclipse.jdt.core.dom.TypeParameter;
import org.eclipse.jdt.core.dom.rewrite.ASTRewrite;
import org.eclipse.jdt.core.dom.rewrite.ImportRewrite.ImportRewriteContext;
import org.eclipse.jdt.core.dom.rewrite.ImportRewrite.TypeLocation;
import org.eclipse.jdt.internal.core.manipulation.dom.ASTResolving;
import org.eclipse.jdt.internal.corext.codemanipulation.StubUtility;
import org.eclipse.jdt.internal.corext.dom.ASTNodes;
import org.eclipse.jdt.internal.corext.dom.Bindings;
import org.eclipse.jdt.internal.corext.util.JavaModelUtil;
import org.eclipse.jdt.internal.corext.util.JdtFlags;
import org.eclipse.jdt.internal.ui.text.correction.ModifierCorrectionSubProcessor;
public class NewMethodCorrectionProposal extends AbstractMethodCorrectionProposal {
private static final String KEY_NAME= "name"; //$NON-NLS-1$
private static final String KEY_TYPE= "type"; //$NON-NLS-1$
private List<Expression> fArguments;
// invocationNode is MethodInvocation, ConstructorInvocation, SuperConstructorInvocation, ClassInstanceCreation, SuperMethodInvocation
public NewMethodCorrectionProposal(String label, ICompilationUnit targetCU, ASTNode invocationNode, List<Expression> arguments, ITypeBinding binding, int relevance, Image image) {
super(label, targetCU, invocationNode, binding, relevance, image);
fArguments= arguments;
}
private int evaluateModifiers(ASTNode targetTypeDecl) {
if (getSenderBinding().isAnnotation()) {
return 0;
}
boolean isTargetInterface= getSenderBinding().isInterface();
if (isTargetInterface && !JavaModelUtil.is18OrHigher(getCompilationUnit().getJavaProject())) {
// only abstract methods are allowed for interface present in less than Java 1.8
return getInterfaceMethodModifiers(targetTypeDecl, true);
}
ASTNode invocationNode= getInvocationNode();
if (invocationNode instanceof MethodInvocation) {
int modifiers= 0;
Expression expression= ((MethodInvocation)invocationNode).getExpression();
if (expression != null) {
if (expression instanceof Name && ((Name) expression).resolveBinding().getKind() == IBinding.TYPE) {
modifiers |= Modifier.STATIC;
}
} else if (ASTResolving.isInStaticContext(invocationNode)) {
modifiers |= Modifier.STATIC;
}
ASTNode node= ASTResolving.findParentType(invocationNode);
boolean isParentInterface= node instanceof TypeDeclaration && ((TypeDeclaration) node).isInterface();
if (isTargetInterface || isParentInterface) {
if (expression == null && !targetTypeDecl.equals(node)) {
modifiers|= Modifier.STATIC;
if (isTargetInterface) {
modifiers|= getInterfaceMethodModifiers(targetTypeDecl, false);
} else {
modifiers|= Modifier.PROTECTED;
}
} else if (modifiers == Modifier.STATIC) {
modifiers= getInterfaceMethodModifiers(targetTypeDecl, false) | Modifier.STATIC;
} else {
modifiers= getInterfaceMethodModifiers(targetTypeDecl, true);
}
} else if (targetTypeDecl.equals(node)) {
modifiers |= Modifier.PRIVATE;
} else if (node instanceof AnonymousClassDeclaration && ASTNodes.isParent(node, targetTypeDecl)) {
modifiers |= Modifier.PROTECTED;
if (ASTResolving.isInStaticContext(node) && expression == null) {
modifiers |= Modifier.STATIC;
}
} else {
modifiers |= Modifier.PUBLIC;
}
return modifiers;
}
return Modifier.PUBLIC;
}
private int getInterfaceMethodModifiers(ASTNode targetTypeDecl, boolean createAbstractMethod) {
// for interface and annotation members copy the modifiers from an existing member
if (targetTypeDecl instanceof TypeDeclaration) {
TypeDeclaration type= (TypeDeclaration) targetTypeDecl;
MethodDeclaration[] methodDecls= type.getMethods();
if (methodDecls.length > 0) {
if (createAbstractMethod) {
for (MethodDeclaration methodDeclaration : methodDecls) {
IMethodBinding methodBinding= methodDeclaration.resolveBinding();
if (methodBinding != null && JdtFlags.isAbstract(methodBinding)) {
return methodDeclaration.getModifiers();
}
}
}
return methodDecls[0].getModifiers() & Modifier.PUBLIC;
}
List<BodyDeclaration> bodyDecls= type.bodyDeclarations();
if (bodyDecls.size() > 0) {
return bodyDecls.get(0).getModifiers() & Modifier.PUBLIC;
}
}
return 0;
}
@Override
protected void addNewModifiers(ASTRewrite rewrite, ASTNode targetTypeDecl, List<IExtendedModifier> modifiers) {
modifiers.addAll(rewrite.getAST().newModifiers(evaluateModifiers(targetTypeDecl)));
ModifierCorrectionSubProcessor.installLinkedVisibilityProposals(getLinkedProposalModel(), rewrite, modifiers, getSenderBinding().isInterface());
}
@Override
protected boolean isConstructor() {
ASTNode node= getInvocationNode();
return node.getNodeType() != ASTNode.METHOD_INVOCATION && node.getNodeType() != ASTNode.SUPER_METHOD_INVOCATION;
}
@Override
protected SimpleName getNewName(ASTRewrite rewrite) {
ASTNode invocationNode= getInvocationNode();
String name;
if (invocationNode instanceof MethodInvocation) {
name= ((MethodInvocation)invocationNode).getName().getIdentifier();
} else if (invocationNode instanceof SuperMethodInvocation) {
name= ((SuperMethodInvocation)invocationNode).getName().getIdentifier();
} else {
name= getSenderBinding().getName(); // name of the class
}
AST ast= rewrite.getAST();
SimpleName newNameNode= ast.newSimpleName(name);
addLinkedPosition(rewrite.track(newNameNode), false, KEY_NAME);
ASTNode invocationName= getInvocationNameNode();
if (invocationName != null && invocationName.getAST() == ast) { // in the same CU
addLinkedPosition(rewrite.track(invocationName), true, KEY_NAME);
}
return newNameNode;
}
private ASTNode getInvocationNameNode() {
ASTNode node= getInvocationNode();
if (node instanceof MethodInvocation) {
return ((MethodInvocation)node).getName();
} else if (node instanceof SuperMethodInvocation) {
return ((SuperMethodInvocation)node).getName();
} else if (node instanceof ClassInstanceCreation) {
Type type= ((ClassInstanceCreation)node).getType();
while (type instanceof ParameterizedType) {
type= ((ParameterizedType) type).getType();
}
return type;
}
return null;
}
@Override
protected Type getNewMethodType(ASTRewrite rewrite, ImportRewriteContext importRewriteContext) throws CoreException {
ASTNode node= getInvocationNode();
AST ast= rewrite.getAST();
Type newTypeNode= null;
ITypeBinding[] otherProposals= null;
if (node.getParent() instanceof MethodInvocation) {
MethodInvocation parent= (MethodInvocation) node.getParent();
if (parent.getExpression() == node) {
ITypeBinding[] bindings= ASTResolving.getQualifierGuess(node.getRoot(), parent.getName().getIdentifier(), parent.arguments(), getSenderBinding());
if (bindings.length > 0) {
newTypeNode= getImportRewrite().addImport(bindings[0], ast, importRewriteContext, TypeLocation.RETURN_TYPE);
otherProposals= bindings;
}
}
}
if (newTypeNode == null) {
ITypeBinding binding= ASTResolving.guessBindingForReference(node);
if (binding != null && binding.isWildcardType()) {
binding= ASTResolving.normalizeWildcardType(binding, false, ast);
}
if (binding != null) {
newTypeNode= getImportRewrite().addImport(binding, ast, importRewriteContext, TypeLocation.RETURN_TYPE);
} else {
ASTNode parent= node.getParent();
if (parent instanceof ExpressionStatement) {
newTypeNode= ast.newPrimitiveType(PrimitiveType.VOID);
} else {
newTypeNode= org.eclipse.jdt.internal.ui.text.correction.ASTResolving.guessTypeForReference(ast, node);
if (newTypeNode == null) {
newTypeNode= ast.newSimpleType(ast.newSimpleName("Object")); //$NON-NLS-1$
}
}
}
}
addLinkedPosition(rewrite.track(newTypeNode), false, KEY_TYPE);
if (otherProposals != null) {
for (int i= 0; i < otherProposals.length; i++) {
addLinkedPositionProposal(KEY_TYPE, otherProposals[i]);
}
}
return newTypeNode;
}
@Override
protected void addNewParameters(ASTRewrite rewrite, List<String> takenNames, List<SingleVariableDeclaration> params, ImportRewriteContext context) throws CoreException {
AST ast= rewrite.getAST();
List<Expression> arguments= fArguments;
for (int i= 0; i < arguments.size(); i++) {
Expression elem= arguments.get(i);
SingleVariableDeclaration param= ast.newSingleVariableDeclaration();
// argument type
String argTypeKey= "arg_type_" + i; //$NON-NLS-1$
Type type= evaluateParameterType(ast, elem, argTypeKey, context);
param.setType(type);
// argument name
String argNameKey= "arg_name_" + i; //$NON-NLS-1$
String name= evaluateParameterName(takenNames, elem, type, argNameKey);
param.setName(ast.newSimpleName(name));
params.add(param);
addLinkedPosition(rewrite.track(param.getType()), false, argTypeKey);
addLinkedPosition(rewrite.track(param.getName()), false, argNameKey);
}
}
private Type evaluateParameterType(AST ast, Expression elem, String key, ImportRewriteContext context) {
ITypeBinding binding= Bindings.normalizeTypeBinding(elem.resolveTypeBinding());
if (binding != null && binding.isWildcardType()) {
binding= ASTResolving.normalizeWildcardType(binding, true, ast);
}
if (binding != null) {
ITypeBinding[] typeProposals= ASTResolving.getRelaxingTypes(ast, binding);
for (int i= 0; i < typeProposals.length; i++) {
addLinkedPositionProposal(key, typeProposals[i]);
}
return getImportRewrite().addImport(binding, ast, context, TypeLocation.PARAMETER);
}
return ast.newSimpleType(ast.newSimpleName("Object")); //$NON-NLS-1$
}
private String evaluateParameterName(List<String> takenNames, Expression argNode, Type type, String key) {
IJavaProject project= getCompilationUnit().getJavaProject();
String[] names= StubUtility.getVariableNameSuggestions(NamingConventions.VK_PARAMETER, project, type, argNode, takenNames);
for (int i= 0; i < names.length; i++) {
addLinkedPositionProposal(key, names[i], null);
}
String favourite= names[0];
takenNames.add(favourite);
return favourite;
}
@Override
protected void addNewExceptions(ASTRewrite rewrite, List<Type> exceptions, ImportRewriteContext context) throws CoreException {
}
@Override
protected void addNewTypeParameters(ASTRewrite rewrite, List<String> takenNames, List<TypeParameter> params, ImportRewriteContext context) throws CoreException {
}
}