blob: 7c56af33e68a66807a323f3aff068a30d1d8ba09 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2000, 2003 IBM Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Common Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/cpl-v10.html
*
* Contributors:
* IBM Corporation - initial API and implementation
*******************************************************************************/
package org.eclipse.jdt.internal.ui.text.correction;
import java.util.HashSet;
import java.util.List;
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.*;
import org.eclipse.jdt.ui.PreferenceConstants;
import org.eclipse.jdt.internal.corext.codemanipulation.StubUtility;
import org.eclipse.jdt.internal.corext.dom.ASTNodeConstants;
import org.eclipse.jdt.internal.corext.dom.ASTNodeFactory;
import org.eclipse.jdt.internal.corext.dom.ASTRewrite;
import org.eclipse.jdt.internal.corext.dom.Bindings;
import org.eclipse.jdt.internal.corext.dom.ScopeAnalyzer;
import org.eclipse.jdt.internal.ui.JavaPluginImages;
/**
* Proposals for 'Assign to variable' quick assist
* - Assign an expression from an ExpressionStatement to a local or field
* - Assign a parameter to a field
* */
public class AssignToVariableAssistProposal extends LinkedCorrectionProposal {
public static final int LOCAL= 1;
public static final int FIELD= 2;
private final String KEY_NAME= "name"; //$NON-NLS-1$
private final String KEY_TYPE= "type"; //$NON-NLS-1$
private final int fVariableKind;
private final ASTNode fNodeToAssign; // ExpressionStatement or SingleVariableDeclaration
private final ITypeBinding fTypeBinding;
public AssignToVariableAssistProposal(ICompilationUnit cu, int variableKind, ExpressionStatement node, ITypeBinding typeBinding, int relevance) {
super(null, cu, null, relevance, null);
fVariableKind= variableKind;
fNodeToAssign= node;
fTypeBinding= typeBinding;
if (variableKind == LOCAL) {
setDisplayName(CorrectionMessages.getString("AssignToVariableAssistProposal.assigntolocal.description")); //$NON-NLS-1$
setImage(JavaPluginImages.get(JavaPluginImages.IMG_CORRECTION_LOCAL));
} else {
setDisplayName(CorrectionMessages.getString("AssignToVariableAssistProposal.assigntofield.description")); //$NON-NLS-1$
setImage(JavaPluginImages.get(JavaPluginImages.IMG_FIELD_PRIVATE));
}
}
public AssignToVariableAssistProposal(ICompilationUnit cu, SingleVariableDeclaration parameter, ITypeBinding typeBinding, int relevance) {
super(null, cu, null, relevance, null);
fVariableKind= FIELD;
fNodeToAssign= parameter;
fTypeBinding= typeBinding;
setDisplayName(CorrectionMessages.getString("AssignToVariableAssistProposal.assignparamtofield.description")); //$NON-NLS-1$
setImage(JavaPluginImages.get(JavaPluginImages.IMG_FIELD_PRIVATE));
}
protected ASTRewrite getRewrite() throws CoreException {
if (fVariableKind == FIELD) {
return doAddField();
} else { // LOCAL
return doAddLocal();
}
}
private ASTRewrite doAddLocal() throws CoreException {
Expression expression= ((ExpressionStatement) fNodeToAssign).getExpression();
ASTRewrite rewrite= new ASTRewrite(fNodeToAssign.getParent());
AST ast= fNodeToAssign.getAST();
String varName= suggestLocalVariableNames(fTypeBinding);
VariableDeclarationFragment newDeclFrag= ast.newVariableDeclarationFragment();
newDeclFrag.setName(ast.newSimpleName(varName));
newDeclFrag.setInitializer((Expression) rewrite.createCopy(expression));
VariableDeclarationStatement newDecl= ast.newVariableDeclarationStatement(newDeclFrag);
Type type= evaluateType(ast);
newDecl.setType(type);
rewrite.markAsReplaced(fNodeToAssign, newDecl);
markAsLinked(rewrite, newDeclFrag.getName(), true, KEY_NAME); //$NON-NLS-1$
markAsLinked(rewrite, newDecl.getType(), false, KEY_TYPE); //$NON-NLS-1$
markAsSelection(rewrite, newDecl);
return rewrite;
}
private ASTRewrite doAddField() throws CoreException {
boolean isParamToField= fNodeToAssign.getNodeType() == ASTNode.SINGLE_VARIABLE_DECLARATION;
ASTNode newTypeDecl= ASTResolving.findParentType(fNodeToAssign);
if (newTypeDecl == null) {
return null;
}
Expression expression= isParamToField ? ((SingleVariableDeclaration) fNodeToAssign).getName() : ((ExpressionStatement) fNodeToAssign).getExpression();
boolean isAnonymous= newTypeDecl.getNodeType() == ASTNode.ANONYMOUS_CLASS_DECLARATION;
List decls= isAnonymous ? ((AnonymousClassDeclaration) newTypeDecl).bodyDeclarations() : ((TypeDeclaration) newTypeDecl).bodyDeclarations();
ASTRewrite rewrite= new ASTRewrite(newTypeDecl);
AST ast= newTypeDecl.getAST();
BodyDeclaration bodyDecl= ASTResolving.findParentBodyDeclaration(fNodeToAssign);
Block body;
if (bodyDecl instanceof MethodDeclaration) {
body= ((MethodDeclaration) bodyDecl).getBody();
} else if (bodyDecl instanceof Initializer) {
body= ((Initializer) bodyDecl).getBody();
} else {
return null;
}
boolean isStatic= Modifier.isStatic(bodyDecl.getModifiers()) && !isAnonymous;
int modifiers= Modifier.PRIVATE;
if (isStatic) {
modifiers |= Modifier.STATIC;
}
String varName= suggestFieldNames(fTypeBinding, expression, modifiers);
VariableDeclarationFragment newDeclFrag= ast.newVariableDeclarationFragment();
newDeclFrag.setName(ast.newSimpleName(varName));
FieldDeclaration newDecl= ast.newFieldDeclaration(newDeclFrag);
Type type= evaluateType(ast);
newDecl.setType(type);
newDecl.setModifiers(modifiers);
Assignment assignment= ast.newAssignment();
assignment.setRightHandSide((Expression) rewrite.createCopy(expression));
boolean needsThis= PreferenceConstants.getPreferenceStore().getBoolean(PreferenceConstants.CODEGEN_KEYWORD_THIS);
if (isParamToField) {
needsThis |= varName.equals(((SimpleName) expression).getIdentifier());
}
SimpleName accessName= ast.newSimpleName(varName);
if (needsThis) {
FieldAccess fieldAccess= ast.newFieldAccess();
fieldAccess.setName(accessName);
if (isStatic) {
String typeName= ((TypeDeclaration) newTypeDecl).getName().getIdentifier();
fieldAccess.setExpression(ast.newSimpleName(typeName));
} else {
fieldAccess.setExpression(ast.newThisExpression());
}
assignment.setLeftHandSide(fieldAccess);
} else {
assignment.setLeftHandSide(accessName);
}
int insertIndex= findFieldInsertIndex(decls, fNodeToAssign.getStartPosition());
rewrite.markAsInsertInOriginal(newTypeDecl, ASTNodeConstants.BODY_DECLARATIONS, newDecl, insertIndex, null);
ASTNode selectionNode;
if (isParamToField) {
// assign parameter to field
ExpressionStatement statement= ast.newExpressionStatement(assignment);
int insertIdx= findAssignmentInsertIndex(body.statements());
rewrite.markAsInsertInOriginal(body, ASTNodeConstants.STATEMENTS, statement, insertIdx, null);
selectionNode= statement;
} else {
rewrite.markAsReplaced(expression, assignment);
selectionNode= fNodeToAssign;
}
markAsLinked(rewrite, newDeclFrag.getName(), false, KEY_NAME);
markAsLinked(rewrite, newDecl.getType(), false, KEY_TYPE);
markAsLinked(rewrite, accessName, true, KEY_NAME);
markAsSelection(rewrite, selectionNode);
return rewrite;
}
private Type evaluateType(AST ast) throws CoreException {
ITypeBinding[] proposals= ASTResolving.getRelaxingTypes(ast, fTypeBinding);
for (int i= 0; i < proposals.length; i++) {
addLinkedModeProposal(KEY_TYPE, proposals[i]);
}
String typeName= getImportRewrite().addImport(fTypeBinding);
return ASTNodeFactory.newType(ast, typeName);
}
private String suggestLocalVariableNames(ITypeBinding binding) {
IJavaProject project= getCompilationUnit().getJavaProject();
ITypeBinding base= binding.isArray() ? binding.getElementType() : binding;
IPackageBinding packBinding= base.getPackage();
String packName= packBinding != null ? packBinding.getName() : ""; //$NON-NLS-1$
String[] excludedNames= getUsedVariableNames();
String typeName= base.getName();
String[] names= NamingConventions.suggestLocalVariableNames(project, packName, typeName, binding.getDimensions(), excludedNames);
if (names.length == 0) {
return "class1"; // fix for pr, remoev after 20030127 //$NON-NLS-1$
}
for (int i= 0; i < names.length; i++) {
addLinkedModeProposal(KEY_NAME, names[i]);
}
return names[0];
}
private String suggestFieldNames(ITypeBinding binding, Expression expression, int modifiers) {
IJavaProject project= getCompilationUnit().getJavaProject();
ITypeBinding base= binding.isArray() ? binding.getElementType() : binding;
IPackageBinding packBinding= base.getPackage();
String packName= packBinding != null ? packBinding.getName() : ""; //$NON-NLS-1$
String[] excludedNames= getUsedVariableNames();
String result= null;
HashSet taken= new HashSet();
if (expression instanceof SimpleName) {
String name= ((SimpleName) expression).getIdentifier();
// bug 38111
String[] argname= StubUtility.getFieldNameSuggestions(project, name, modifiers, excludedNames);
for (int i= 0; i < argname.length; i++) {
String curr= argname[i];
if (result == null || curr.length() > result.length()) {
result= curr;
}
if (taken.add(curr)) {
addLinkedModeProposal(KEY_NAME, curr);
}
}
}
String typeName= base.getName();
String[] names= NamingConventions.suggestFieldNames(project, packName, typeName, binding.getDimensions(), modifiers, excludedNames);
if (names.length == 0) {
return "class1"; // fix for pr, remoev after 20030127 //$NON-NLS-1$
}
for (int i= 0; i < names.length; i++) {
String curr= names[i];
if (taken.add(curr)) {
addLinkedModeProposal(KEY_NAME, curr);
}
}
if (result == null) {
result= names[0];
}
return result;
}
private String[] getUsedVariableNames() {
CompilationUnit root= (CompilationUnit) fNodeToAssign.getRoot();
IBinding[] bindings= (new ScopeAnalyzer(root)).getDeclarationsInScope(fNodeToAssign.getStartPosition(), ScopeAnalyzer.VARIABLES);
String[] names= new String[bindings.length];
for (int i= 0; i < names.length; i++) {
names[i]= bindings[i].getName();
}
return names;
}
private int findAssignmentInsertIndex(List statements) {
HashSet paramsBefore= new HashSet();
List params = ((MethodDeclaration) fNodeToAssign.getParent()).parameters();
for (int i = 0; i < params.size() && (params.get(i) != fNodeToAssign); i++) {
SingleVariableDeclaration decl= (SingleVariableDeclaration) params.get(i);
paramsBefore.add(decl.getName().getIdentifier());
}
int i= 0;
for (i = 0; i < statements.size(); i++) {
Statement curr= (Statement) statements.get(i);
switch (curr.getNodeType()) {
case ASTNode.CONSTRUCTOR_INVOCATION:
case ASTNode.SUPER_CONSTRUCTOR_INVOCATION:
break;
case ASTNode.EXPRESSION_STATEMENT:
Expression expr= ((ExpressionStatement) curr).getExpression();
if (expr instanceof Assignment) {
Assignment assignment= (Assignment) expr;
Expression rightHand = assignment.getRightHandSide();
if (rightHand instanceof SimpleName && paramsBefore.contains(((SimpleName) rightHand).getIdentifier())) {
IVariableBinding binding = Bindings.getAssignedVariable(assignment);
if (binding == null || binding.isField()) {
break;
}
}
}
return i;
default:
return i;
}
}
return i;
}
private int findFieldInsertIndex(List decls, int currPos) {
for (int i= decls.size() - 1; i >= 0; i--) {
ASTNode curr= (ASTNode) decls.get(i);
if (curr instanceof FieldDeclaration && currPos > curr.getStartPosition() + curr.getLength()) {
return i + 1;
}
}
return 0;
}
/**
* Returns the variable kind.
* @return int
*/
public int getVariableKind() {
return fVariableKind;
}
}