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