| /******************************************************************************* |
| * Copyright (c) 2000, 2006 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 |
| *******************************************************************************/ |
| package org.eclipse.jdt.internal.corext.fix; |
| |
| import java.util.ArrayList; |
| import java.util.Iterator; |
| import java.util.List; |
| |
| import org.eclipse.text.edits.TextEditGroup; |
| |
| import org.eclipse.core.runtime.CoreException; |
| |
| import org.eclipse.jdt.core.ICompilationUnit; |
| import org.eclipse.jdt.core.IJavaProject; |
| import org.eclipse.jdt.core.dom.AST; |
| import org.eclipse.jdt.core.dom.ASTNode; |
| import org.eclipse.jdt.core.dom.ASTVisitor; |
| import org.eclipse.jdt.core.dom.ArrayAccess; |
| import org.eclipse.jdt.core.dom.Assignment; |
| import org.eclipse.jdt.core.dom.Block; |
| import org.eclipse.jdt.core.dom.CompilationUnit; |
| 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.ITypeBinding; |
| import org.eclipse.jdt.core.dom.InfixExpression; |
| 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.SuperFieldAccess; |
| import org.eclipse.jdt.core.dom.Type; |
| import org.eclipse.jdt.core.dom.VariableDeclaration; |
| 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.ITrackedNodePosition; |
| import org.eclipse.jdt.core.dom.rewrite.ImportRewrite; |
| import org.eclipse.jdt.core.dom.rewrite.ListRewrite; |
| |
| import org.eclipse.jdt.internal.corext.codemanipulation.StubUtility; |
| import org.eclipse.jdt.internal.corext.dom.ASTNodeFactory; |
| import org.eclipse.jdt.internal.corext.dom.ASTNodes; |
| import org.eclipse.jdt.internal.corext.dom.Bindings; |
| import org.eclipse.jdt.internal.corext.dom.ModifierRewrite; |
| import org.eclipse.jdt.internal.corext.dom.ScopeAnalyzer; |
| import org.eclipse.jdt.internal.corext.fix.LinkedFix.AbstractLinkedFixRewriteOperation; |
| import org.eclipse.jdt.internal.corext.refactoring.structure.CompilationUnitRewrite; |
| import org.eclipse.jdt.internal.corext.util.JavaModelUtil; |
| |
| |
| public class ConvertForLoopOperation extends AbstractLinkedFixRewriteOperation { |
| |
| private ForStatement fOldForStatement; |
| private EnhancedForStatement fEnhancedForStatement; |
| private AST fAst; |
| private Name fCollectionName; |
| private SingleVariableDeclaration fParameterDeclaration; |
| private ITypeBinding fOldCollectionTypeBinding; |
| private IBinding fOldCollectionBinding; |
| private IBinding fIndexBinding; |
| private boolean fCollectionIsMethodCall= false; |
| private MethodInvocation fMethodInvocation; |
| private final String fParameterName; |
| private final ICompilationUnit fCompilationUnit; |
| private FieldAccess fFieldAccess; |
| private final CompilationUnit fRoot; |
| private final boolean fAddBlock; |
| private final boolean fRemoveUnnecessaryBlocks; |
| |
| /** |
| * Visitor class for finding all references to a certain Name within the |
| * specified scope (e.g. finds all references to a local variable within the |
| * Body of a For loop). |
| */ |
| private class LocalOccurencesFinder extends ASTVisitor { |
| |
| private List fOccurences; |
| private ASTNode fScope; |
| private IBinding fTempBinding; |
| private ITypeBinding fTempTypeBinding; |
| |
| /** |
| * @param collectionName The inferred name of the collection to be |
| * iterated over |
| * @param oldCollectionBinding The binding of the inferred collection |
| * @param oldCollectionTypeBinding The type binding of the inferred |
| * collection |
| * @param scope The scope of the search (i.e. the body of a For |
| * Statement |
| */ |
| public LocalOccurencesFinder(Name collectionName, IBinding oldCollectionBinding, ITypeBinding oldCollectionTypeBinding, |
| ASTNode scope) { |
| this.fScope= scope; |
| fOccurences= new ArrayList(); |
| fTempBinding= oldCollectionBinding; |
| fTempTypeBinding= oldCollectionTypeBinding; |
| } |
| |
| public LocalOccurencesFinder(Name name, ASTNode scope) { |
| this.fScope= scope; |
| fOccurences= new ArrayList(); |
| fTempBinding= name.resolveBinding(); |
| } |
| |
| public LocalOccurencesFinder(IBinding binding, ASTNode scope) { |
| this.fScope= scope; |
| fOccurences= new ArrayList(); |
| fTempBinding= binding; |
| } |
| |
| public void perform() { |
| fScope.accept(this); |
| } |
| |
| public boolean visit(SimpleName node) { |
| if (node.getParent() instanceof VariableDeclaration) { |
| if (((VariableDeclaration)node.getParent()).getName() == node) |
| return true; //don't include declaration |
| } |
| if (fTempBinding != null && Bindings.equals(fTempBinding, node.resolveBinding())) { |
| fOccurences.add(node); |
| } |
| return true; |
| } |
| |
| public boolean visit(MethodInvocation methodInvocation) { |
| ArrayAccess arrayAccess= (ArrayAccess)ASTNodes.getParent(methodInvocation, ArrayAccess.class); |
| if (arrayAccess != null && fTempTypeBinding != null |
| && Bindings.equals(fTempBinding, methodInvocation.resolveMethodBinding())) { |
| fOccurences.add(arrayAccess); |
| return false; |
| } |
| return true; |
| } |
| |
| public List getOccurences() { |
| return fOccurences; |
| } |
| } |
| |
| /** |
| * @param forStatement The For statement to be converted |
| * @param root |
| * @param addBlock |
| * @param removeUnnecessaryBlocks |
| */ |
| public ConvertForLoopOperation(CompilationUnit root, ForStatement forStatement, String parameterName, boolean addBlock, boolean removeUnnecessaryBlocks) { |
| fRoot= root; |
| fAddBlock= addBlock; |
| fRemoveUnnecessaryBlocks= removeUnnecessaryBlocks; |
| fCompilationUnit= (ICompilationUnit)root.getJavaElement(); |
| this.fOldForStatement= forStatement; |
| fAst= root.getAST(); |
| fParameterName= parameterName; |
| } |
| |
| /** |
| * Check if the OldFor can be converted to Enhanced For. Unless all |
| * preconditions hold true, there is no reason for this QuickAssist to pop |
| * up. |
| * |
| * @return true if all preconditions (arrayCanBeInferred && |
| * arrayOrIndexNotAssignedTo indexNotReferencedOutsideInferredArray && |
| * onlyOneIndexUsed && additionalTempsNotReferenced) are satisfied |
| */ |
| public boolean satisfiesPreconditions() { |
| return JavaModelUtil.is50OrHigher(fCompilationUnit.getJavaProject()) |
| && fOldForStatement.getExpression() != null |
| && arrayCanBeInferred() |
| && typeBindingsAreNotNull() |
| && bodySatifiesPreconditions() |
| && initializersSatisfyPreconditions() |
| && updatersSatifyPreconditions(); |
| } |
| |
| private boolean typeBindingsAreNotNull() { |
| fIndexBinding= getIndexBinding(); |
| return fOldCollectionBinding != null && fOldCollectionTypeBinding != null && fIndexBinding != null; |
| } |
| |
| private boolean bodySatifiesPreconditions() { |
| // checks in a single pass through Loop's body that arrayOrIndexNotAssignedTo |
| // and indexNotReferencedOutsideInferredArray |
| final List writeAccesses= new ArrayList(); |
| final boolean isIndexReferenced[]= {false}; |
| |
| fOldForStatement.getBody().accept(new ASTVisitor() { |
| public boolean visit(Assignment assignment) { |
| classifyWriteAccess(assignment.getLeftHandSide()); |
| return true; |
| } |
| public boolean visit(PostfixExpression node) { |
| classifyWriteAccess(node.getOperand()); |
| return true; |
| } |
| public boolean visit(PrefixExpression node) { |
| classifyWriteAccess(node.getOperand()); |
| return true; |
| } |
| public boolean visit(SimpleName name) { |
| IBinding binding= name.resolveBinding(); |
| if (Bindings.equals(fIndexBinding, binding)) { |
| ASTNode parent= name.getParent(); |
| // check if the direct parent is an ArrayAcces |
| if (parent instanceof ArrayAccess){ |
| // even if the Index is referenced within an ArrayAccess |
| // it could happen that the Array is not the same as the |
| // inferred Array |
| |
| // On fixing bug https://bugs.eclipse.org/bugs/show_bug.cgi?id=73890 |
| // had to treat the case when indexNotReferenced flag does not get overridden |
| // by subsequent passes through this loop |
| isIndexReferenced[0]= isIndexReferenced[0] || isAccessToADifferentArray((ArrayAccess)parent); |
| } |
| else { |
| //otherwise the Index is referenced outside ArrayAccess |
| isIndexReferenced[0]= true; |
| } |
| } |
| return false; |
| } |
| private void classifyWriteAccess(Expression expression) { |
| //check that |
| if (expression instanceof ArrayAccess) { |
| checkThatArrayIsNotAssigned(writeAccesses, expression); |
| } else if (expression instanceof Name) { |
| checkThatIndexIsNotAssigned(writeAccesses, expression); |
| } |
| } |
| }); |
| return writeAccesses.isEmpty() && !isIndexReferenced[0]; |
| } |
| |
| private void checkThatIndexIsNotAssigned(final List writeAccesses, Expression expression) { |
| Name name= (Name)expression; |
| IBinding binding= name.resolveBinding(); |
| if (binding == fIndexBinding) { |
| writeAccesses.add(name); |
| } |
| } |
| |
| private void checkThatArrayIsNotAssigned(final List writeAccesses, Expression expression) { |
| ArrayAccess arrayAccess= (ArrayAccess)expression; |
| Expression array= arrayAccess.getArray(); |
| IBinding binding= null; |
| if (array instanceof Name) { |
| binding= ((Name)array).resolveBinding(); |
| } else if (array instanceof FieldAccess) { |
| binding= ((FieldAccess)array).resolveFieldBinding(); |
| } else if (array instanceof SuperFieldAccess) { |
| binding= ((SuperFieldAccess)array).resolveFieldBinding(); |
| } |
| if (binding == fOldCollectionBinding) |
| writeAccesses.add(arrayAccess); |
| } |
| |
| private boolean isAccessToADifferentArray(ArrayAccess arrayAccess) { |
| Expression expression= arrayAccess.getArray(); |
| if (expression instanceof Name) { |
| return isNameDifferentThanInferredArray((Name)expression); |
| } else if (expression instanceof FieldAccess){ |
| FieldAccess fieldAccess= (FieldAccess)expression; |
| return isNameDifferentThanInferredArray(fieldAccess.getName()); |
| } else if (expression instanceof MethodInvocation){ |
| //Be more conservative here, just because it's the same name |
| //does not imply that it is the same array. The method can |
| //return different arrays. It could even be a method call on |
| //a different object. https://bugs.eclipse.org/bugs/show_bug.cgi?id=138353 |
| return true; |
| }else { |
| return true; //conservative approach: if it doesn't fall within the above cases |
| // I return that it's an access to a different Array (causing the precondition |
| // to fail) |
| } |
| } |
| |
| private boolean isNameDifferentThanInferredArray(Name name) { |
| IBinding arrayBinding= name.resolveBinding(); |
| if (!Bindings.equals(fOldCollectionBinding, arrayBinding)) { |
| return true; |
| } |
| return false; |
| } |
| |
| private boolean updatersSatifyPreconditions() { |
| return onlyOneIndexUsed() && indexNotDecremented(); |
| } |
| |
| private boolean indexNotDecremented() { |
| ASTNode updater= (ASTNode)fOldForStatement.updaters().get(0); |
| |
| if (updater instanceof PostfixExpression) { |
| if ("++".equals(((PostfixExpression)updater).getOperator().toString())) //$NON-NLS-1$ |
| return true; |
| } |
| |
| if (updater instanceof PrefixExpression){ |
| if ("++".equals(((PrefixExpression)updater).getOperator().toString())) //$NON-NLS-1$ |
| return true; |
| } |
| return false; |
| } |
| |
| private boolean initializersSatisfyPreconditions(){ |
| // Only one pass through Initializers |
| // check if startsFromZero and additionalTempsNotReferenced |
| |
| final List tempVarsInInitializers= new ArrayList(); |
| final boolean startsFromZero[] = {false}; |
| List initializers= fOldForStatement.initializers(); |
| |
| for (Iterator iter = initializers.iterator(); iter.hasNext();) { |
| Expression element = (Expression) iter.next(); |
| if (!(element instanceof VariableDeclarationExpression)) |
| return false; |
| |
| element.accept(new ASTVisitor(){ |
| public boolean visit(VariableDeclarationFragment declarationFragment){ |
| Name indexName= declarationFragment.getName(); |
| tempVarsInInitializers.add(indexName); |
| startsFromZero[0]= doesIndexStartFromZero(indexName, declarationFragment); |
| return false; |
| } |
| public boolean visit(Assignment assignment){ |
| if (assignment.getLeftHandSide() instanceof Name) { |
| Name indexName= (Name) assignment.getLeftHandSide(); |
| tempVarsInInitializers.add(indexName); |
| startsFromZero[0]= doesIndexStartFromZero(indexName, assignment); |
| } |
| return false; |
| } |
| }); |
| } |
| |
| removeInferredIndexFrom(tempVarsInInitializers); |
| |
| return startsFromZero[0] && additionalTempsNotReferenced(tempVarsInInitializers); |
| } |
| |
| private boolean doesIndexStartFromZero(Name indexName, ASTNode declaringNode) { |
| IBinding binding= indexName.resolveBinding(); |
| if (Bindings.equals(fIndexBinding, binding)){ |
| Expression initializer = null; |
| if (declaringNode instanceof VariableDeclarationFragment){ |
| initializer= ((VariableDeclarationFragment)declaringNode).getInitializer(); |
| } else if (declaringNode instanceof Assignment){ |
| initializer= ((Assignment) declaringNode).getRightHandSide(); |
| } |
| |
| if (initializer instanceof NumberLiteral){ |
| NumberLiteral number= (NumberLiteral) initializer; |
| if (! "0".equals(number.getToken())) { //$NON-NLS-1$ |
| return false; |
| } |
| } else { |
| //variable or method call, most probably not 0... |
| //could also be a constant which is 0, but who would define such a constant? |
| return false; |
| } |
| } |
| return true; // we have to return true also for the cases when we test another variable besides |
| // Inferred Index |
| } |
| |
| |
| |
| private void removeInferredIndexFrom(List localTemps) { |
| Name indexName= null; |
| for (Iterator iter= localTemps.iterator(); iter.hasNext();) { |
| Name name= (Name)iter.next(); |
| IBinding binding= name.resolveBinding(); |
| //fIndexBinding has already been initialized via typeBindingsAreNotNull() |
| if (Bindings.equals(fIndexBinding, binding)) { |
| indexName= name; |
| break; |
| } |
| } |
| localTemps.remove(indexName); |
| } |
| |
| private boolean additionalTempsNotReferenced(List localTemps) { |
| for (Iterator iter= localTemps.iterator(); iter.hasNext();) { |
| Name name= (Name)iter.next(); |
| LocalOccurencesFinder finder= new LocalOccurencesFinder(name, fOldForStatement.getBody()); |
| finder.perform(); |
| if (!finder.getOccurences().isEmpty()) |
| return false; |
| } |
| return true; |
| } |
| |
| private boolean onlyOneIndexUsed() { |
| return fOldForStatement.updaters().size() == 1; |
| } |
| |
| private boolean arrayCanBeInferred() { |
| doInferCollection(); |
| return (fCollectionName != null) |
| && fOldCollectionTypeBinding != null |
| // for now, only iteration over Arrays are handled |
| && (fOldCollectionTypeBinding.isArray()); |
| } |
| |
| private IBinding inferIndexBinding() { |
| List initializers= fOldForStatement.initializers(); |
| if (initializers.size() == 0) |
| return null; |
| |
| Expression expression= (Expression)initializers.get(0); |
| if (expression instanceof VariableDeclarationExpression) { |
| VariableDeclarationFragment declaration= (VariableDeclarationFragment)((VariableDeclarationExpression)expression) |
| .fragments().get(0); |
| Name indexName= declaration.getName(); |
| fIndexBinding= indexName.resolveBinding(); |
| } else if (expression instanceof Assignment) { |
| Assignment assignment= (Assignment)expression; |
| Expression lhs= assignment.getLeftHandSide(); |
| if (lhs instanceof Name) { |
| Name indexName= (Name)lhs; |
| fIndexBinding= indexName.resolveBinding(); |
| } |
| } |
| return fIndexBinding; |
| } |
| |
| private ITrackedNodePosition doConvert(ASTRewrite rewrite, ImportRewrite importRewrite, TextEditGroup group) throws CoreException { |
| doInferCollection(); |
| doInferElement(importRewrite); |
| doFindAndReplaceInBody(rewrite, group); |
| |
| AST ast= fOldForStatement.getAST(); |
| fEnhancedForStatement= ast.newEnhancedForStatement(); |
| if (fAddBlock && !(fOldForStatement.getBody() instanceof Block)) { |
| Statement theBody= (Statement)rewrite.createMoveTarget(fOldForStatement.getBody()); |
| Block newBody= ast.newBlock(); |
| ListRewrite listRewrite= rewrite.getListRewrite(newBody, Block.STATEMENTS_PROPERTY); |
| listRewrite.insertFirst(theBody, group); |
| fEnhancedForStatement.setBody(newBody); |
| } else if (fRemoveUnnecessaryBlocks && fOldForStatement.getBody() instanceof Block && ((Block)fOldForStatement.getBody()).statements().size() == 1) { |
| Statement moveTarget= (Statement)rewrite.createMoveTarget((Statement)((Block)fOldForStatement.getBody()).statements().get(0)); |
| fEnhancedForStatement.setBody(moveTarget); |
| } else { |
| Statement theBody= (Statement)rewrite.createMoveTarget(fOldForStatement.getBody()); |
| fEnhancedForStatement.setBody(theBody); |
| } |
| fEnhancedForStatement.setExpression(createExpression(rewrite, ast)); |
| fEnhancedForStatement.setParameter(fParameterDeclaration); |
| PositionGroup pg= getPositionGroup(fParameterName); |
| pg.addFirstPosition(rewrite.track(fParameterDeclaration.getName())); |
| |
| String name= fParameterDeclaration.getName().getIdentifier(); |
| |
| List proposals= getProposalsForElement(); |
| if (!proposals.contains(name)) |
| proposals.add(0, name); |
| |
| for (Iterator iterator= proposals.iterator(); iterator.hasNext();) |
| pg.addProposal((String) iterator.next(), null); |
| |
| rewrite.replace(fOldForStatement, fEnhancedForStatement, group); |
| return null; |
| } |
| |
| private Expression createExpression(ASTRewrite rewrite, AST ast) { |
| if (fCollectionIsMethodCall) { |
| MethodInvocation methodCall= (MethodInvocation) rewrite.createMoveTarget(fMethodInvocation); |
| return methodCall; |
| } else |
| if (fFieldAccess != null) { |
| return (FieldAccess)rewrite.createMoveTarget(fFieldAccess); |
| } |
| return fCollectionName; |
| } |
| |
| private List getProposalsForElement() { |
| List list= new ArrayList(); |
| ICompilationUnit icu= fCompilationUnit; |
| IJavaProject javaProject= icu.getJavaProject(); |
| int dimensions= fOldCollectionTypeBinding.getDimensions() - 1; |
| final List used= getUsedVariableNames(); |
| String type= fOldCollectionTypeBinding.getName(); |
| if (fOldCollectionTypeBinding.isArray()) |
| type= fOldCollectionTypeBinding.getElementType().getName(); |
| String[] proposals= StubUtility.getLocalNameSuggestions(javaProject, type, dimensions, (String[]) used.toArray(new String[used.size()])); |
| for (int i= 0; i < proposals.length; i++) { |
| list.add(proposals[i]); |
| } |
| return list; |
| } |
| |
| private List getUsedVariableNames() { |
| CompilationUnit root= (CompilationUnit)fOldForStatement.getRoot(); |
| IBinding[] varsBefore= (new ScopeAnalyzer(root)).getDeclarationsInScope(fOldForStatement.getStartPosition(), |
| ScopeAnalyzer.VARIABLES); |
| IBinding[] varsAfter= (new ScopeAnalyzer(root)).getDeclarationsAfter(fOldForStatement.getStartPosition() |
| + fOldForStatement.getLength(), ScopeAnalyzer.VARIABLES); |
| |
| List names= new ArrayList(); |
| for (int i= 0; i < varsBefore.length; i++) { |
| names.add(varsBefore[i].getName()); |
| } |
| for (int i= 0; i < varsAfter.length; i++) { |
| names.add(varsAfter[i].getName()); |
| } |
| return names; |
| } |
| |
| private void doFindAndReplaceInBody(ASTRewrite rewrite, TextEditGroup group) { |
| LocalOccurencesFinder finder= new LocalOccurencesFinder(fCollectionName, fOldCollectionBinding, |
| fOldCollectionTypeBinding, fOldForStatement.getBody()); |
| finder.perform(); |
| List occurences= finder.getOccurences(); |
| |
| // this might be the "ideal" case (exercised in testNiceReduction) |
| if (occurences.size() == 1) { |
| ASTNode soleOccurence= (ASTNode)occurences.get(0); |
| ArrayAccess arrayAccess= soleOccurence instanceof ArrayAccess |
| ? (ArrayAccess)soleOccurence |
| : (ArrayAccess)ASTNodes.getParent(soleOccurence, ArrayAccess.class); |
| if (arrayAccess != null) { |
| if (arrayAccess.getParent() instanceof VariableDeclarationFragment) { |
| replaceSingleVariableDeclaration(rewrite, arrayAccess, group); |
| return; |
| } |
| } |
| } |
| |
| replaceMultipleOccurences(rewrite, occurences, group); |
| } |
| |
| private void replaceSingleVariableDeclaration(ASTRewrite rewrite, ArrayAccess arrayAccess, TextEditGroup group) { |
| VariableDeclarationFragment declarationFragment= (VariableDeclarationFragment)arrayAccess.getParent(); |
| VariableDeclarationStatement declarationStatement= (VariableDeclarationStatement)declarationFragment.getParent(); |
| |
| // if could not infer THE_ELEMENT from infer step, we might |
| // be able to infer it from here |
| if (fParameterDeclaration == null) { |
| fParameterDeclaration= fAst.newSingleVariableDeclaration(); |
| } |
| |
| SimpleName theTempVariable= declarationFragment.getName(); |
| |
| SimpleName name= fAst.newSimpleName(theTempVariable.getIdentifier()); |
| Type type= ASTNodeFactory.newType(getAst(), declarationFragment); |
| fParameterDeclaration.setName(name); |
| fParameterDeclaration.setType(type); |
| if (ASTNodes.findModifierNode(Modifier.FINAL, declarationStatement.modifiers()) != null) { |
| ModifierRewrite.create(rewrite, fParameterDeclaration).setModifiers(Modifier.FINAL, Modifier.NONE, group); |
| } |
| |
| |
| LocalOccurencesFinder finder2= new LocalOccurencesFinder(theTempVariable.resolveBinding(), fOldForStatement.getBody()); |
| finder2.perform(); |
| List occurences2= finder2.getOccurences(); |
| |
| linkAllReferences(rewrite, occurences2); |
| |
| rewrite.replace(declarationStatement, null, group); |
| return; |
| } |
| |
| private void linkAllReferences(ASTRewrite rewrite, List occurences) { |
| for (Iterator iter= occurences.iterator(); iter.hasNext();) { |
| ASTNode variableRef= (ASTNode)iter.next(); |
| getPositionGroup(fParameterName).addPosition(rewrite.track(variableRef)); |
| } |
| } |
| |
| private void replaceMultipleOccurences(ASTRewrite rewrite, List occurences, TextEditGroup group) { |
| for (Iterator iter= occurences.iterator(); iter.hasNext();) { |
| ASTNode element= (ASTNode)iter.next(); |
| ArrayAccess arrayAccess= element instanceof ArrayAccess ? (ArrayAccess)element : (ArrayAccess)ASTNodes.getParent( |
| element, ArrayAccess.class); |
| if (arrayAccess != null) { |
| SimpleName elementReference= fAst.newSimpleName(fParameterDeclaration.getName().getIdentifier()); |
| |
| rewrite.replace(arrayAccess, elementReference, group); |
| getPositionGroup(fParameterName).addPosition(rewrite.track(elementReference)); |
| |
| } |
| } |
| } |
| |
| private void doInferElement(ImportRewrite importRewrite) throws CoreException { |
| if (fCollectionName == null) { |
| createDefaultParameter(); |
| } else { |
| if (fOldCollectionTypeBinding.isArray()) { |
| final ITypeBinding elementType= fOldCollectionTypeBinding.getElementType(); |
| fParameterDeclaration= fAst.newSingleVariableDeclaration(); |
| SimpleName name= fAst.newSimpleName(fParameterName); |
| fParameterDeclaration.setName(name); |
| |
| Type theType= importType(elementType, fOldForStatement, importRewrite, fRoot); |
| if (fOldCollectionTypeBinding.getDimensions() != 1) { |
| theType= fAst.newArrayType(theType, fOldCollectionTypeBinding.getDimensions() - 1); |
| } |
| fParameterDeclaration.setType(theType); |
| } |
| } |
| } |
| |
| private void createDefaultParameter() { |
| fParameterDeclaration= fAst.newSingleVariableDeclaration(); |
| SimpleName name= fAst.newSimpleName(fParameterName); |
| Type type= fAst.newPrimitiveType(PrimitiveType.INT); |
| fParameterDeclaration.setName(name); |
| fParameterDeclaration.setType(type); |
| } |
| |
| // Caches the inferred collection name and its bindings in local fields. These |
| // won't change during the whole operation of the QuickFix. |
| private void doInferCollection() { |
| if (fCollectionName != null) |
| return; |
| |
| doInferCollectionFromExpression(); |
| |
| if (fCollectionName == null) |
| doInferCollectionFromInitializers(); |
| |
| } |
| |
| private void doInferCollectionFromExpression() { |
| Expression stopCondition= fOldForStatement.getExpression(); |
| if (stopCondition.getNodeType() == ASTNode.INFIX_EXPRESSION) { |
| Expression rightOperand= ((InfixExpression)stopCondition).getRightOperand(); |
| if (rightOperand.getNodeType() == ASTNode.QUALIFIED_NAME) { |
| Name qualifier= ((QualifiedName)rightOperand).getQualifier(); |
| fCollectionName= ASTNodeFactory.newName(fAst,qualifier.getFullyQualifiedName()); |
| fOldCollectionBinding= qualifier.resolveBinding(); |
| fOldCollectionTypeBinding= qualifier.resolveTypeBinding(); |
| } else if (rightOperand.getNodeType() == ASTNode.METHOD_INVOCATION) { |
| MethodInvocation methodCall= (MethodInvocation)rightOperand; |
| Expression exp= methodCall.getExpression(); |
| if (exp instanceof Name) { |
| Name collectionName= (Name)exp; |
| fOldCollectionBinding= collectionName.resolveBinding(); |
| fOldCollectionTypeBinding= collectionName.resolveTypeBinding(); |
| fCollectionName= ASTNodeFactory.newName(fAst,collectionName.getFullyQualifiedName()); |
| } |
| } else if (rightOperand instanceof FieldAccess){ |
| // this treats the case when the stop condition is a method call or field access |
| // which returns an Array on which the "length" field is queried |
| FieldAccess fieldAccess= (FieldAccess) rightOperand; |
| if ("length".equals(fieldAccess.getName().getIdentifier())) { //$NON-NLS-1$ |
| if (fieldAccess.getExpression() instanceof MethodInvocation){ |
| fCollectionIsMethodCall= true; |
| MethodInvocation methodCall= (MethodInvocation) fieldAccess.getExpression(); |
| fMethodInvocation= methodCall; |
| fOldCollectionBinding= methodCall.resolveMethodBinding(); |
| fOldCollectionTypeBinding= methodCall.resolveTypeBinding(); |
| fCollectionName= ASTNodeFactory.newName(fAst, methodCall.getName().getFullyQualifiedName()); |
| } else if (fieldAccess.getExpression() instanceof FieldAccess) { |
| FieldAccess fieldCall= (FieldAccess)fieldAccess.getExpression(); |
| fFieldAccess= fieldCall; |
| fOldCollectionBinding= fieldCall.resolveFieldBinding(); |
| fOldCollectionTypeBinding= fieldCall.resolveTypeBinding(); |
| fCollectionName= ASTNodeFactory.newName(fAst, fieldCall.getName().getFullyQualifiedName()); |
| } |
| } |
| |
| } |
| } |
| } |
| |
| private void doInferCollectionFromInitializers() { |
| List initializers= fOldForStatement.initializers(); |
| for (Iterator iter= initializers.iterator(); iter.hasNext();) { |
| Object next= iter.next(); |
| if (next instanceof VariableDeclarationExpression) { |
| VariableDeclarationExpression element= (VariableDeclarationExpression)next; |
| List declarationFragments= element.fragments(); |
| for (Iterator iterator= declarationFragments.iterator(); iterator.hasNext();) { |
| VariableDeclarationFragment fragment= (VariableDeclarationFragment)iterator.next(); |
| Expression initializer= fragment.getInitializer(); |
| if (initializer != null) |
| doInferCollectionFromExpression(initializer); |
| } |
| } else if (next instanceof Assignment) { |
| Assignment assignemnt= (Assignment)next; |
| doInferCollectionFromExpression(assignemnt.getRightHandSide()); |
| } |
| } |
| } |
| |
| /** |
| * @param expression Expression to visit. This helper method is useful |
| * for the IDIOM when the stop condition is expressed with another |
| * variable within loop: for (int i=0, max= array.length; i < max; |
| * i++){} |
| */ |
| private void doInferCollectionFromExpression(Expression expression) { |
| final boolean[] foundMoreThenOneArray= new boolean[1]; |
| foundMoreThenOneArray[0]= false; |
| expression.accept(new ASTVisitor() { |
| public boolean visit(QualifiedName qualifiedName) { |
| initializeBindings(qualifiedName.getQualifier()); |
| return false; |
| } |
| public boolean visit(SimpleName simpleName) { |
| initializeBindings(simpleName); |
| return false; |
| } |
| public boolean visit(MethodInvocation methodCall){ |
| ITypeBinding typeBinding= methodCall.resolveTypeBinding(); |
| if (typeBinding.isArray()){ |
| fCollectionIsMethodCall= true; |
| fMethodInvocation= methodCall; |
| fOldCollectionTypeBinding= typeBinding; |
| fOldCollectionBinding= methodCall.resolveMethodBinding(); |
| fCollectionName= ASTNodeFactory.newName(fAst, methodCall.getName().getFullyQualifiedName()); |
| } |
| return false; |
| } |
| public boolean visit(FieldAccess field) { |
| if (initializeBindings(field.getName())) { |
| fFieldAccess= field; |
| } |
| return true; |
| } |
| private boolean initializeBindings(Name name) { |
| ITypeBinding typeBinding= name.resolveTypeBinding(); |
| if (typeBinding != null && typeBinding.isArray()) { |
| fOldCollectionTypeBinding= typeBinding; |
| if (fOldCollectionBinding == null) { |
| fOldCollectionBinding= name.resolveBinding(); |
| fCollectionName= ASTNodeFactory.newName(fAst,name.getFullyQualifiedName()); |
| return true; |
| } else { |
| if (name.resolveBinding() != fOldCollectionBinding) { |
| foundMoreThenOneArray[0]= true; |
| } |
| return true; |
| } |
| } |
| return false; |
| } |
| }); |
| if (foundMoreThenOneArray[0]) { |
| fOldCollectionBinding= null; |
| fCollectionName= null; |
| } |
| } |
| |
| private AST getAst() { |
| return fAst; |
| } |
| |
| // lazy load. Caches the binding of the For's index in a field since it cannot |
| // be change during the whole QuickFix |
| private IBinding getIndexBinding(){ |
| if (fIndexBinding != null) |
| return fIndexBinding; |
| else return inferIndexBinding(); |
| } |
| |
| /* (non-Javadoc) |
| * @see org.eclipse.jdt.internal.corext.fix.LinkedFix.ILinkedFixRewriteOperation#rewriteAST(org.eclipse.jdt.internal.corext.refactoring.structure.CompilationUnitRewrite, java.util.List, java.util.List) |
| */ |
| public ITrackedNodePosition rewriteAST(CompilationUnitRewrite cuRewrite, List textEditGroups, List positionGroups) throws CoreException { |
| TextEditGroup group= createTextEditGroup(FixMessages.Java50Fix_ConvertToEnhancedForLoop_description); |
| textEditGroups.add(group); |
| clearPositionGroups(); |
| ITrackedNodePosition endPosition= doConvert(cuRewrite.getASTRewrite(), cuRewrite.getImportRewrite(), group); |
| positionGroups.addAll(getAllPositionGroups()); |
| return endPosition; |
| } |
| |
| } |