blob: 2256b01a7f68d17b43bcd6bb66fc302e41824af8 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2000, 2019 IBM Corporation and others.
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* IBM Corporation - initial API and implementation
*******************************************************************************/
package org.eclipse.jdt.internal.corext.fix;
import java.util.List;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.text.edits.TextEditGroup;
import org.eclipse.jdt.core.IJavaElement;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.dom.AST;
import org.eclipse.jdt.core.dom.ASTMatcher;
import org.eclipse.jdt.core.dom.ASTNode;
import org.eclipse.jdt.core.dom.ArrayAccess;
import org.eclipse.jdt.core.dom.Assignment;
import org.eclipse.jdt.core.dom.CompilationUnit;
import org.eclipse.jdt.core.dom.ContinueStatement;
import org.eclipse.jdt.core.dom.EnhancedForStatement;
import org.eclipse.jdt.core.dom.Expression;
import org.eclipse.jdt.core.dom.FieldAccess;
import org.eclipse.jdt.core.dom.ForStatement;
import org.eclipse.jdt.core.dom.IBinding;
import org.eclipse.jdt.core.dom.IMethodBinding;
import org.eclipse.jdt.core.dom.ITypeBinding;
import org.eclipse.jdt.core.dom.IVariableBinding;
import org.eclipse.jdt.core.dom.InfixExpression;
import org.eclipse.jdt.core.dom.InfixExpression.Operator;
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.NumberLiteral;
import org.eclipse.jdt.core.dom.PostfixExpression;
import org.eclipse.jdt.core.dom.PrefixExpression;
import org.eclipse.jdt.core.dom.PrimitiveType;
import org.eclipse.jdt.core.dom.QualifiedName;
import org.eclipse.jdt.core.dom.SimpleName;
import org.eclipse.jdt.core.dom.SingleVariableDeclaration;
import org.eclipse.jdt.core.dom.Statement;
import org.eclipse.jdt.core.dom.ThisExpression;
import org.eclipse.jdt.core.dom.Type;
import org.eclipse.jdt.core.dom.VariableDeclarationExpression;
import org.eclipse.jdt.core.dom.VariableDeclarationFragment;
import org.eclipse.jdt.core.dom.VariableDeclarationStatement;
import org.eclipse.jdt.core.dom.rewrite.ASTRewrite;
import org.eclipse.jdt.core.dom.rewrite.ImportRewrite;
import org.eclipse.jdt.core.dom.rewrite.ImportRewrite.TypeLocation;
import org.eclipse.jdt.core.dom.rewrite.ListRewrite;
import org.eclipse.jdt.internal.core.manipulation.StubUtility;
import org.eclipse.jdt.internal.corext.dom.ASTNodes;
import org.eclipse.jdt.internal.corext.dom.GenericVisitor;
import org.eclipse.jdt.internal.corext.dom.JdtASTMatcher;
import org.eclipse.jdt.internal.corext.dom.ModifierRewrite;
import org.eclipse.jdt.internal.corext.refactoring.structure.CompilationUnitRewrite;
import org.eclipse.jdt.internal.corext.refactoring.util.TightSourceRangeComputer;
import org.eclipse.jdt.internal.corext.util.JavaModelUtil;
public class ConvertForLoopOperation extends ConvertLoopOperation {
private static final String LENGTH_QUERY= "length"; //$NON-NLS-1$
private static final String SIZE_QUERY= "size"; //$NON-NLS-1$
private static final String GET_QUERY= "get"; //$NON-NLS-1$
private static final String ISEMPTY_QUERY= "isEmpty"; //$NON-NLS-1$
private static final String LITERAL_0= "0"; //$NON-NLS-1$
private static final String LITERAL_1= "1"; //$NON-NLS-1$
private static final class InvalidBodyError extends Error {
private static final long serialVersionUID= 1L;
}
private IVariableBinding fIndexBinding;
private IVariableBinding fLengthBinding;
private IBinding fArrayBinding;
private Expression fArrayAccess;
private VariableDeclarationFragment fElementDeclaration;
private boolean fMakeFinal;
private boolean fIsCollection;
private IMethodBinding fSizeMethodBinding;
private IMethodBinding fGetMethodBinding;
private MethodInvocation fSizeMethodAccess;
public ConvertForLoopOperation(ForStatement forStatement) {
this(forStatement, new String[0], false);
}
public ConvertForLoopOperation(ForStatement forStatement, String[] usedNames, boolean makeFinal) {
super(forStatement, usedNames);
fMakeFinal= makeFinal;
}
@Override
public IStatus satisfiesPreconditions() {
ForStatement statement= getForStatement();
CompilationUnit ast= (CompilationUnit)statement.getRoot();
IJavaElement javaElement= ast.getJavaElement();
if (javaElement == null)
return ERROR_STATUS;
if (!JavaModelUtil.is50OrHigher(javaElement.getJavaProject()))
return ERROR_STATUS;
if (!validateInitializers(statement))
return ERROR_STATUS;
if (!validateExpression(statement))
return ERROR_STATUS;
if (!validateUpdaters(statement))
return ERROR_STATUS;
if (!validateBody(statement))
return ERROR_STATUS;
return Status.OK_STATUS;
}
/*
* Must be one of:
* <ul>
* <li>int [result]= 0;</li>
* <li>int [result]= 0, [lengthBinding]= [arrayBinding].length;</li>
* <li>int , [result]= 0;</li>
* </ul>
*/
private boolean validateInitializers(ForStatement statement) {
List<Expression> initializers= statement.initializers();
if (initializers.size() != 1)
return false;
Expression expression= initializers.get(0);
if (!(expression instanceof VariableDeclarationExpression))
return false;
VariableDeclarationExpression declaration= (VariableDeclarationExpression)expression;
ITypeBinding declarationBinding= declaration.resolveTypeBinding();
if (declarationBinding == null)
return false;
if (!declarationBinding.isPrimitive())
return false;
if (!PrimitiveType.INT.toString().equals(declarationBinding.getQualifiedName()))
return false;
List<VariableDeclarationFragment> fragments= declaration.fragments();
if (fragments.size() == 1) {
IVariableBinding indexBinding= getIndexBindingFromFragment(fragments.get(0));
if (indexBinding == null)
return false;
fIndexBinding= indexBinding;
return true;
} else if (fragments.size() == 2) {
IVariableBinding indexBinding= getIndexBindingFromFragment(fragments.get(0));
if (indexBinding == null) {
indexBinding= getIndexBindingFromFragment(fragments.get(1));
if (indexBinding == null)
return false;
if (!validateLengthFragment(fragments.get(0)))
return false;
} else {
if (!validateLengthFragment(fragments.get(1)))
return false;
}
fIndexBinding= indexBinding;
return true;
}
return false;
}
/*
* [lengthBinding]= [arrayBinding].length
*/
private boolean validateLengthFragment(VariableDeclarationFragment fragment) {
Expression initializer= fragment.getInitializer();
if (initializer == null)
return false;
if (!validateLengthQuery(initializer))
return false;
IVariableBinding lengthBinding= (IVariableBinding)fragment.getName().resolveBinding();
if (lengthBinding == null)
return false;
fLengthBinding= lengthBinding;
return true;
}
/*
* Must be one of:
* <ul>
* <li>[result]= 0</li>
* </ul>
*/
private IVariableBinding getIndexBindingFromFragment(VariableDeclarationFragment fragment) {
Expression initializer= fragment.getInitializer();
if (!(initializer instanceof NumberLiteral))
return null;
NumberLiteral number= (NumberLiteral)initializer;
if (!LITERAL_0.equals(number.getToken()))
return null;
return (IVariableBinding)fragment.getName().resolveBinding();
}
/*
* Must be one of:
* <ul>
* <li>[indexBinding] < [result].length;</li>
* <li>[result].length > [indexBinding];</li>
* <li>[indexBinding] < [lengthBinding];</li>
* <li>[lengthBinding] > [indexBinding];</li>
* </ul>
*/
private boolean validateExpression(ForStatement statement) {
Expression expression= statement.getExpression();
if (!(expression instanceof InfixExpression))
return false;
InfixExpression infix= (InfixExpression)expression;
Expression left= infix.getLeftOperand();
Expression right= infix.getRightOperand();
if (left instanceof SimpleName && right instanceof SimpleName) {
IVariableBinding lengthBinding= fLengthBinding;
if (lengthBinding == null)
return false;
IBinding leftBinding= ((SimpleName)left).resolveBinding();
IBinding righBinding= ((SimpleName)right).resolveBinding();
if (fIndexBinding.equals(leftBinding)) {
return lengthBinding.equals(righBinding);
} else if (fIndexBinding.equals(righBinding)) {
return lengthBinding.equals(leftBinding);
}
return false;
} else if (left instanceof SimpleName) {
if (!fIndexBinding.equals(((SimpleName)left).resolveBinding()))
return false;
if (!Operator.LESS.equals(infix.getOperator()))
return false;
return validateLengthQuery(right);
} else if (right instanceof SimpleName) {
if (!fIndexBinding.equals(((SimpleName)right).resolveBinding()))
return false;
if (!Operator.GREATER.equals(infix.getOperator()))
return false;
return validateLengthQuery(left);
}
return false;
}
/*
* Must be one of:
* <ul>
* <li>[result].length</li>
* <li>[result].size()</li>
* </ul>
*/
private boolean validateLengthQuery(Expression lengthQuery) {
if (lengthQuery instanceof QualifiedName) {
QualifiedName qualifiedName= (QualifiedName)lengthQuery;
SimpleName name= qualifiedName.getName();
if (!LENGTH_QUERY.equals(name.getIdentifier()))
return false;
Name arrayAccess= qualifiedName.getQualifier();
ITypeBinding accessType= arrayAccess.resolveTypeBinding();
if (accessType == null)
return false;
if (!accessType.isArray())
return false;
IBinding arrayBinding= arrayAccess.resolveBinding();
if (arrayBinding == null)
return false;
fArrayBinding= arrayBinding;
fArrayAccess= arrayAccess;
return true;
} else if (lengthQuery instanceof FieldAccess) {
FieldAccess fieldAccess= (FieldAccess)lengthQuery;
SimpleName name= fieldAccess.getName();
if (!LENGTH_QUERY.equals(name.getIdentifier()))
return false;
Expression arrayAccess= fieldAccess.getExpression();
ITypeBinding accessType= arrayAccess.resolveTypeBinding();
if (accessType == null)
return false;
if (!accessType.isArray())
return false;
IBinding arrayBinding= getBinding(arrayAccess);
if (arrayBinding == null)
return false;
fArrayBinding= arrayBinding;
fArrayAccess= arrayAccess;
return true;
} else if (lengthQuery instanceof MethodInvocation) {
MethodInvocation methodCall= (MethodInvocation)lengthQuery;
SimpleName name= methodCall.getName();
if (!SIZE_QUERY.equals(name.getIdentifier())
|| !methodCall.arguments().isEmpty()) {
return false;
}
IMethodBinding methodBinding= methodCall.resolveMethodBinding();
if (methodBinding == null) {
return false;
}
ITypeBinding classBinding= methodBinding.getDeclaringClass();
if (isCollection(classBinding)) {
fIsCollection= true;
fSizeMethodBinding= methodBinding;
fSizeMethodAccess= methodCall;
return true;
}
}
return false;
}
private boolean isCollection(ITypeBinding classBinding) {
ITypeBinding[] interfaces= classBinding.getInterfaces();
for (ITypeBinding binding : interfaces) {
if (binding.getErasure().getQualifiedName().startsWith("java.util.Collection")) { //$NON-NLS-1$
return true;
}
}
ITypeBinding superClass= classBinding.getSuperclass();
if (superClass != null && isCollection(superClass)) {
return true;
}
for (ITypeBinding binding : interfaces) {
if (isCollection(binding)) {
return true;
}
}
return false;
}
/*
* Must be one of:
* <ul>
* <li>[indexBinding]++</li>
* <li>++[indexBinding]</li>
* <li>[indexBinding]+= 1</li>
* <li>[indexBinding]= [indexBinding] + 1</li>
* <li>[indexBinding]= 1 + [indexBinding]</li>
* <ul>
*/
private boolean validateUpdaters(ForStatement statement) {
List<Expression> updaters= statement.updaters();
if (updaters.size() != 1)
return false;
Expression updater= updaters.get(0);
if (updater instanceof PostfixExpression) {
PostfixExpression postfix= (PostfixExpression)updater;
if (!PostfixExpression.Operator.INCREMENT.equals(postfix.getOperator()))
return false;
IBinding binding= getBinding(postfix.getOperand());
if (!fIndexBinding.equals(binding))
return false;
return true;
} else if (updater instanceof PrefixExpression) {
PrefixExpression prefix= (PrefixExpression) updater;
if (!PrefixExpression.Operator.INCREMENT.equals(prefix.getOperator()))
return false;
IBinding binding= getBinding(prefix.getOperand());
if (!fIndexBinding.equals(binding))
return false;
return true;
} else if (updater instanceof Assignment) {
Assignment assignment= (Assignment)updater;
Expression left= assignment.getLeftHandSide();
IBinding binding= getBinding(left);
if (!fIndexBinding.equals(binding))
return false;
if (Assignment.Operator.PLUS_ASSIGN.equals(assignment.getOperator())) {
return isOneLiteral(assignment.getRightHandSide());
} else if (Assignment.Operator.ASSIGN.equals(assignment.getOperator())) {
Expression right= assignment.getRightHandSide();
if (!(right instanceof InfixExpression))
return false;
InfixExpression infixExpression= (InfixExpression)right;
Expression leftOperand= infixExpression.getLeftOperand();
IBinding leftBinding= getBinding(leftOperand);
Expression rightOperand= infixExpression.getRightOperand();
IBinding rightBinding= getBinding(rightOperand);
if (fIndexBinding.equals(leftBinding)) {
return isOneLiteral(rightOperand);
} else if (fIndexBinding.equals(rightBinding)) {
return isOneLiteral(leftOperand);
}
}
}
return false;
}
private boolean isOneLiteral(Expression expression) {
if (!(expression instanceof NumberLiteral))
return false;
NumberLiteral literal= (NumberLiteral)expression;
return LITERAL_1.equals(literal.getToken());
}
/*
* returns false iff
* <ul>
* <li><code>indexBinding</code> is used for anything else then accessing
* an element of <code>arrayBinding</code></li> or as a parameter to <code>getBinding</code>
* <li><code>arrayBinding</code> is assigned</li>
* <li>an element of <code>arrayBinding</code> is assigned</li>
* <li><code>lengthBinding</code> is referenced</li>
* <li>a method call is made to anything but get(<code>indexBinding</code>) or size() or isEmpty()
* </ul>
* within <code>body</code>
*/
private boolean validateBody(ForStatement statement) {
Statement body= statement.getBody();
try {
body.accept(new GenericVisitor() {
@Override
protected boolean visitNode(ASTNode node) {
if (node instanceof ContinueStatement) {
return false;
}
if (node instanceof Name) {
Name name= (Name)node;
IBinding nameBinding= name.resolveBinding();
if (nameBinding == null)
throw new InvalidBodyError();
if (nameBinding.equals(fIndexBinding)) {
if (node.getLocationInParent() == ArrayAccess.INDEX_PROPERTY) {
if (fIsCollection)
throw new InvalidBodyError();
ArrayAccess arrayAccess= (ArrayAccess)node.getParent();
Expression array= arrayAccess.getArray();
if (array instanceof QualifiedName) {
if (!(fArrayAccess instanceof QualifiedName))
throw new InvalidBodyError();
IBinding varBinding1= ((QualifiedName) array).getQualifier().resolveBinding();
if (varBinding1 == null)
throw new InvalidBodyError();
IBinding varBinding2= ((QualifiedName) fArrayAccess).getQualifier().resolveBinding();
if (!varBinding1.equals(varBinding2))
throw new InvalidBodyError();
} else if (array instanceof FieldAccess) {
Expression arrayExpression= ((FieldAccess) array).getExpression();
if (arrayExpression instanceof ThisExpression) {
if (fArrayAccess instanceof FieldAccess) {
Expression arrayAccessExpression= ((FieldAccess) fArrayAccess).getExpression();
if (!(arrayAccessExpression instanceof ThisExpression))
throw new InvalidBodyError();
} else if (fArrayAccess instanceof QualifiedName) {
throw new InvalidBodyError();
}
} else {
if (!(fArrayAccess instanceof FieldAccess))
throw new InvalidBodyError();
Expression arrayAccessExpression= ((FieldAccess) fArrayAccess).getExpression();
if (!arrayExpression.subtreeMatch(new JdtASTMatcher(), arrayAccessExpression)) {
throw new InvalidBodyError();
}
}
} else {
if (fArrayAccess instanceof QualifiedName) {
throw new InvalidBodyError();
}
if (fArrayAccess instanceof FieldAccess) {
Expression arrayAccessExpression= ((FieldAccess) fArrayAccess).getExpression();
if (!(arrayAccessExpression instanceof ThisExpression))
throw new InvalidBodyError();
}
}
IBinding binding= getBinding(array);
if (binding == null)
throw new InvalidBodyError();
if (!fArrayBinding.equals(binding))
throw new InvalidBodyError();
} else if (node.getLocationInParent() == MethodInvocation.ARGUMENTS_PROPERTY) {
MethodInvocation method= (MethodInvocation)node.getParent();
IMethodBinding methodBinding= method.resolveMethodBinding();
if (methodBinding == null)
throw new InvalidBodyError();
ITypeBinding[] parms= methodBinding.getParameterTypes();
if (!fIsCollection || !GET_QUERY.equals(method.getName().getFullyQualifiedName()) ||
parms.length != 1 || !parms[0].getName().equals("int") || //$NON-NLS-1$
!fSizeMethodBinding.getDeclaringClass().equals(methodBinding.getDeclaringClass()) ||
fSizeMethodAccess.getExpression() == null ||
!fSizeMethodAccess.getExpression().subtreeMatch(new ASTMatcher(), method.getExpression()))
throw new InvalidBodyError();
fGetMethodBinding= methodBinding;
} else {
throw new InvalidBodyError();
}
} else if (nameBinding.equals(fArrayBinding)) {
if (isAssigned(node))
throw new InvalidBodyError();
} else if (nameBinding.equals(fLengthBinding)) {
throw new InvalidBodyError();
} else if (fElementDeclaration != null && nameBinding.equals(fElementDeclaration.getName().resolveBinding())) {
if (isAssigned(node))
fElementDeclaration= null;
}
} else if (fIsCollection && node instanceof MethodInvocation) {
MethodInvocation method= (MethodInvocation)node;
IMethodBinding binding= method.resolveMethodBinding();
if (binding == null) {
throw new InvalidBodyError();
}
if (fSizeMethodBinding.getDeclaringClass().equals(binding.getDeclaringClass())) {
String methodName= method.getName().getFullyQualifiedName();
if (!SIZE_QUERY.equals(methodName) &&
!GET_QUERY.equals(methodName) &&
!ISEMPTY_QUERY.equals(methodName)) {
throw new InvalidBodyError();
}
}
}
return true;
}
private boolean isAssigned(ASTNode current) {
while (current != null && !(current instanceof Statement)) {
if (current.getLocationInParent() == Assignment.LEFT_HAND_SIDE_PROPERTY)
return true;
if (current instanceof PrefixExpression
&& !(((PrefixExpression) current).getOperand() instanceof MethodInvocation && ((PrefixExpression) current).getOperator().equals(PrefixExpression.Operator.NOT)))
return true;
if (current instanceof PostfixExpression)
return true;
current= current.getParent();
}
return false;
}
@Override
public boolean visit(ArrayAccess node) {
if (fElementDeclaration != null)
return super.visit(node);
IBinding binding= getBinding(node.getArray());
if (!fIsCollection && fArrayBinding.equals(binding)) {
IBinding index= getBinding(node.getIndex());
if (fIndexBinding.equals(index)) {
if (node.getLocationInParent() == VariableDeclarationFragment.INITIALIZER_PROPERTY) {
fElementDeclaration= (VariableDeclarationFragment)node.getParent();
}
}
}
return super.visit(node);
}
@Override
public boolean visit(MethodInvocation node) {
if (fElementDeclaration != null || !fIsCollection)
return super.visit(node);
IMethodBinding nodeBinding= node.resolveMethodBinding();
if (nodeBinding == null) {
return super.visit(node);
}
ITypeBinding[] args= nodeBinding.getParameterTypes();
if (GET_QUERY.equals(nodeBinding.getName()) && args.length == 1 &&
args[0].getName().equals("int") && //$NON-NLS-1$
nodeBinding.getDeclaringClass().equals(fSizeMethodBinding.getDeclaringClass())) {
IBinding index= getBinding((Expression)node.arguments().get(0));
if (fIndexBinding.equals(index)) {
if (node.getLocationInParent() == VariableDeclarationFragment.INITIALIZER_PROPERTY) {
fElementDeclaration= (VariableDeclarationFragment)node.getParent();
}
}
}
return super.visit(node);
}
});
} catch (InvalidBodyError e) {
return false;
}
return true;
}
private static IBinding getBinding(Expression expression) {
if (expression instanceof FieldAccess) {
return ((FieldAccess)expression).resolveFieldBinding();
} else if (expression instanceof Name) {
return ((Name)expression).resolveBinding();
}
return null;
}
@Override
public String getIntroducedVariableName() {
if (fElementDeclaration != null) {
return fElementDeclaration.getName().getIdentifier();
} else {
ForStatement forStatement= getForStatement();
IJavaProject javaProject= ((CompilationUnit)forStatement.getRoot()).getJavaElement().getJavaProject();
String[] proposals= null;
if (this.fIsCollection) {
proposals= getVariableNameProposalsCollection(fSizeMethodAccess, javaProject);
} else {
proposals= getVariableNameProposals(fArrayAccess.resolveTypeBinding(), javaProject);
}
return proposals[0];
}
}
@Override
public void rewriteAST(CompilationUnitRewrite cuRewrite, LinkedProposalModelCore positionGroups) throws CoreException {
TextEditGroup group= createTextEditGroup(FixMessages.Java50Fix_ConvertToEnhancedForLoop_description, cuRewrite);
ASTRewrite rewrite= cuRewrite.getASTRewrite();
TightSourceRangeComputer rangeComputer;
if (rewrite.getExtendedSourceRangeComputer() instanceof TightSourceRangeComputer) {
rangeComputer= (TightSourceRangeComputer)rewrite.getExtendedSourceRangeComputer();
} else {
rangeComputer= new TightSourceRangeComputer();
}
rangeComputer.addTightSourceNode(getForStatement());
rewrite.setTargetSourceRangeComputer(rangeComputer);
Statement statement= convert(cuRewrite, group, positionGroups);
rewrite.replace(getForStatement(), statement, group);
}
@Override
protected Statement convert(CompilationUnitRewrite cuRewrite, TextEditGroup group, LinkedProposalModelCore positionGroups) throws CoreException {
ASTRewrite rewrite= cuRewrite.getASTRewrite();
ImportRewrite importRewrite= cuRewrite.getImportRewrite();
ForStatement forStatement= getForStatement();
IJavaProject javaProject= ((CompilationUnit)forStatement.getRoot()).getJavaElement().getJavaProject();
String[] proposals= null;
if (this.fIsCollection) {
proposals= getVariableNameProposalsCollection(fSizeMethodAccess, javaProject);
} else {
proposals= getVariableNameProposals(fArrayAccess.resolveTypeBinding(), javaProject);
}
String parameterName;
if (fElementDeclaration != null) {
parameterName= fElementDeclaration.getName().getIdentifier();
} else {
parameterName= proposals[0];
}
LinkedProposalPositionGroupCore pg= positionGroups.getPositionGroup(parameterName, true);
if (fElementDeclaration != null)
pg.addProposal(parameterName, 10);
for (int i= 0; i < proposals.length; i++) {
pg.addProposal(proposals[i], 10);
}
AST ast= forStatement.getAST();
EnhancedForStatement result= ast.newEnhancedForStatement();
SingleVariableDeclaration parameterDeclaration= null;
Expression parameterExpression= null;
if (this.fIsCollection) {
parameterExpression= fSizeMethodAccess.getExpression();
parameterDeclaration= createParameterDeclarationCollection(parameterName, fElementDeclaration, fSizeMethodAccess, forStatement, importRewrite, rewrite, group, pg, fMakeFinal);
} else {
parameterExpression= fArrayAccess;
parameterDeclaration= createParameterDeclaration(parameterName, fElementDeclaration, fArrayAccess, forStatement, importRewrite, rewrite, group, pg, fMakeFinal);
}
result.setParameter(parameterDeclaration);
result.setExpression((Expression)rewrite.createCopyTarget(parameterExpression));
if (this.fIsCollection) {
convertBodyCollection(forStatement.getBody(), fIndexBinding, fGetMethodBinding, parameterName, rewrite, group, pg);
} else {
convertBody(forStatement.getBody(), fIndexBinding, fArrayBinding, parameterName, rewrite, group, pg);
}
result.setBody(getBody(cuRewrite, group, positionGroups));
positionGroups.setEndPosition(rewrite.track(result));
return result;
}
private void convertBody(Statement body, final IBinding indexBinding, final IBinding arrayBinding, final String parameterName, final ASTRewrite rewrite, final TextEditGroup editGroup, final LinkedProposalPositionGroupCore pg) {
final AST ast= body.getAST();
body.accept(new GenericVisitor() {
@Override
public boolean visit(ArrayAccess node) {
IBinding binding= getBinding(node.getArray());
if (arrayBinding.equals(binding)) {
IBinding index= getBinding(node.getIndex());
if (indexBinding.equals(index)) {
replaceAccess(node);
}
}
return super.visit(node);
}
private void replaceAccess(ASTNode node) {
if (fElementDeclaration != null && node.getLocationInParent() == VariableDeclarationFragment.INITIALIZER_PROPERTY) {
VariableDeclarationFragment fragment= (VariableDeclarationFragment)node.getParent();
IBinding targetBinding= fragment.getName().resolveBinding();
if (targetBinding != null) {
VariableDeclarationStatement statement= (VariableDeclarationStatement)fragment.getParent();
if (statement.fragments().size() == 1) {
rewrite.remove(statement, editGroup);
} else {
ListRewrite listRewrite= rewrite.getListRewrite(statement, VariableDeclarationStatement.FRAGMENTS_PROPERTY);
listRewrite.remove(fragment, editGroup);
}
} else {
SimpleName name= ast.newSimpleName(parameterName);
rewrite.replace(node, name, editGroup);
pg.addPosition(rewrite.track(name), true);
}
} else {
SimpleName name= ast.newSimpleName(parameterName);
rewrite.replace(node, name, editGroup);
pg.addPosition(rewrite.track(name), true);
}
}
});
}
private SingleVariableDeclaration createParameterDeclaration(String parameterName, VariableDeclarationFragment fragement, Expression arrayAccess, ForStatement statement, ImportRewrite importRewrite, ASTRewrite rewrite, TextEditGroup group, LinkedProposalPositionGroupCore pg, boolean makeFinal) {
CompilationUnit compilationUnit= (CompilationUnit)arrayAccess.getRoot();
AST ast= compilationUnit.getAST();
SingleVariableDeclaration result= ast.newSingleVariableDeclaration();
SimpleName name= ast.newSimpleName(parameterName);
pg.addPosition(rewrite.track(name), true);
result.setName(name);
ITypeBinding arrayTypeBinding= arrayAccess.resolveTypeBinding();
Type type= importType(arrayTypeBinding.getElementType(), statement, importRewrite, compilationUnit,
arrayTypeBinding.getDimensions() == 1 ? TypeLocation.LOCAL_VARIABLE : TypeLocation.ARRAY_CONTENTS);
if (arrayTypeBinding.getDimensions() != 1) {
type= ast.newArrayType(type, arrayTypeBinding.getDimensions() - 1);
}
result.setType(type);
if (fragement != null) {
VariableDeclarationStatement declaration= (VariableDeclarationStatement)fragement.getParent();
ModifierRewrite.create(rewrite, result).copyAllModifiers(declaration, group);
}
if (makeFinal && (fragement == null || ASTNodes.findModifierNode(Modifier.FINAL, ASTNodes.getModifiers(fragement)) == null)) {
ModifierRewrite.create(rewrite, result).setModifiers(Modifier.FINAL, 0, group);
}
return result;
}
private String[] getVariableNameProposals(ITypeBinding arrayTypeBinding, IJavaProject project) {
String[] variableNames= getUsedVariableNames();
String baseName= FOR_LOOP_ELEMENT_IDENTIFIER;
String name= fArrayBinding.getName();
if (name.length() > 2 && name.charAt(name.length() - 1) == 's') {
baseName= name.substring(0, name.length() - 1);
}
String[] elementSuggestions= StubUtility.getLocalNameSuggestions(project, baseName, 0, variableNames);
String type= arrayTypeBinding.getElementType().getName();
String[] typeSuggestions= StubUtility.getLocalNameSuggestions(project, type, arrayTypeBinding.getDimensions() - 1, variableNames);
String[] result= new String[elementSuggestions.length + typeSuggestions.length];
System.arraycopy(elementSuggestions, 0, result, 0, elementSuggestions.length);
System.arraycopy(typeSuggestions, 0, result, elementSuggestions.length, typeSuggestions.length);
return result;
}
private String[] getVariableNameProposalsCollection(MethodInvocation sizeMethodAccess, IJavaProject project) {
String[] variableNames= getUsedVariableNames();
String baseName= FOR_LOOP_ELEMENT_IDENTIFIER;
Expression exp= sizeMethodAccess.getExpression();
String name= exp instanceof SimpleName ? ((SimpleName)exp).getFullyQualifiedName() : ""; //$NON-NLS-1$
if (name.length() > 2 && name.charAt(name.length() - 1) == 's') {
baseName= name.substring(0, name.length() - 1);
}
String[] elementSuggestions= StubUtility.getLocalNameSuggestions(project, baseName, 0, variableNames);
ITypeBinding[] typeArgs= fSizeMethodBinding.getDeclaringClass().getTypeArguments();
String type= "Object"; //$NON-NLS-1$
if (typeArgs != null && typeArgs.length > 0) {
type= typeArgs[0].getName();
}
String[] typeSuggestions= StubUtility.getLocalNameSuggestions(project, type, 0, variableNames);
String[] result= new String[elementSuggestions.length + typeSuggestions.length];
System.arraycopy(elementSuggestions, 0, result, 0, elementSuggestions.length);
System.arraycopy(typeSuggestions, 0, result, elementSuggestions.length, typeSuggestions.length);
return result;
}
private void convertBodyCollection(Statement body, final IBinding indexBinding, final IBinding getBinding, final String parameterName, final ASTRewrite rewrite, final TextEditGroup editGroup, final LinkedProposalPositionGroupCore pg) {
final AST ast= body.getAST();
body.accept(new GenericVisitor() {
@Override
public boolean visit(MethodInvocation node) {
IBinding binding= node.resolveMethodBinding();
if (binding != null && binding.equals(getBinding)) {
List<Expression> args = node.arguments();
if (args.size() == 1 && args.get(0) instanceof SimpleName
&& indexBinding.equals(((SimpleName)args.get(0)).resolveBinding())) {
replaceAccess(node);
}
}
return super.visit(node);
}
private void replaceAccess(ASTNode node) {
if (fElementDeclaration != null && node.getLocationInParent() == VariableDeclarationFragment.INITIALIZER_PROPERTY) {
VariableDeclarationFragment fragment= (VariableDeclarationFragment)node.getParent();
IBinding targetBinding= fragment.getName().resolveBinding();
if (targetBinding != null) {
VariableDeclarationStatement statement= (VariableDeclarationStatement)fragment.getParent();
if (statement.fragments().size() == 1) {
rewrite.remove(statement, editGroup);
} else {
ListRewrite listRewrite= rewrite.getListRewrite(statement, VariableDeclarationStatement.FRAGMENTS_PROPERTY);
listRewrite.remove(fragment, editGroup);
}
} else {
SimpleName name= ast.newSimpleName(parameterName);
rewrite.replace(node, name, editGroup);
pg.addPosition(rewrite.track(name), true);
}
} else {
SimpleName name= ast.newSimpleName(parameterName);
rewrite.replace(node, name, editGroup);
pg.addPosition(rewrite.track(name), true);
}
}
});
}
private SingleVariableDeclaration createParameterDeclarationCollection(String parameterName, VariableDeclarationFragment fragement, Expression sizeAccess, ForStatement statement, ImportRewrite importRewrite, ASTRewrite rewrite, TextEditGroup group, LinkedProposalPositionGroupCore pg, boolean makeFinal) {
CompilationUnit compilationUnit= (CompilationUnit)sizeAccess.getRoot();
AST ast= compilationUnit.getAST();
SingleVariableDeclaration result= ast.newSingleVariableDeclaration();
SimpleName name= ast.newSimpleName(parameterName);
pg.addPosition(rewrite.track(name), true);
result.setName(name);
IMethodBinding sizeTypeBinding= ((MethodInvocation)sizeAccess).resolveMethodBinding();
ITypeBinding[] sizeTypeArguments= sizeTypeBinding.getDeclaringClass().getTypeArguments();
Type type= importType(sizeTypeArguments != null && sizeTypeArguments.length > 0
? sizeTypeArguments[0]
: fSizeMethodAccess.getAST().resolveWellKnownType("java.lang.Object"), //$NON-NLS-1$
statement, importRewrite, compilationUnit,
TypeLocation.LOCAL_VARIABLE);
result.setType(type);
if (fragement != null) {
VariableDeclarationStatement declaration= (VariableDeclarationStatement)fragement.getParent();
ModifierRewrite.create(rewrite, result).copyAllModifiers(declaration, group);
}
if (makeFinal && (fragement == null || ASTNodes.findModifierNode(Modifier.FINAL, ASTNodes.getModifiers(fragement)) == null)) {
ModifierRewrite.create(rewrite, result).setModifiers(Modifier.FINAL, 0, group);
}
return result;
}
}