| /******************************************************************************* |
| * 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 { |
| } |
| } |