KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > eclipse > jdt > internal > ui > text > correction > ConvertForLoopProposal


1 /*******************************************************************************
2  * Copyright (c) 2000, 2005 IBM Corporation and others.
3  * All rights reserved. This program and the accompanying materials
4  * are made available under the terms of the Eclipse Public License v1.0
5  * which accompanies this distribution, and is available at
6  * http://www.eclipse.org/legal/epl-v10.html
7  *
8  * Contributors:
9  * IBM Corporation - initial API and implementation
10  *******************************************************************************/

11 package org.eclipse.jdt.internal.ui.text.correction;
12
13 import java.util.ArrayList JavaDoc;
14 import java.util.Iterator JavaDoc;
15 import java.util.List JavaDoc;
16
17 import org.eclipse.core.runtime.CoreException;
18
19 import org.eclipse.swt.graphics.Image;
20
21 import org.eclipse.jdt.core.ICompilationUnit;
22 import org.eclipse.jdt.core.IJavaProject;
23 import org.eclipse.jdt.core.dom.AST;
24 import org.eclipse.jdt.core.dom.ASTNode;
25 import org.eclipse.jdt.core.dom.ASTVisitor;
26 import org.eclipse.jdt.core.dom.ArrayAccess;
27 import org.eclipse.jdt.core.dom.Assignment;
28 import org.eclipse.jdt.core.dom.CompilationUnit;
29 import org.eclipse.jdt.core.dom.EnhancedForStatement;
30 import org.eclipse.jdt.core.dom.Expression;
31 import org.eclipse.jdt.core.dom.FieldAccess;
32 import org.eclipse.jdt.core.dom.ForStatement;
33 import org.eclipse.jdt.core.dom.IBinding;
34 import org.eclipse.jdt.core.dom.ITypeBinding;
35 import org.eclipse.jdt.core.dom.InfixExpression;
36 import org.eclipse.jdt.core.dom.MethodInvocation;
37 import org.eclipse.jdt.core.dom.Name;
38 import org.eclipse.jdt.core.dom.NumberLiteral;
39 import org.eclipse.jdt.core.dom.PostfixExpression;
40 import org.eclipse.jdt.core.dom.PrefixExpression;
41 import org.eclipse.jdt.core.dom.PrimitiveType;
42 import org.eclipse.jdt.core.dom.QualifiedName;
43 import org.eclipse.jdt.core.dom.SimpleName;
44 import org.eclipse.jdt.core.dom.SingleVariableDeclaration;
45 import org.eclipse.jdt.core.dom.Statement;
46 import org.eclipse.jdt.core.dom.Type;
47 import org.eclipse.jdt.core.dom.VariableDeclaration;
48 import org.eclipse.jdt.core.dom.VariableDeclarationExpression;
49 import org.eclipse.jdt.core.dom.VariableDeclarationFragment;
50 import org.eclipse.jdt.core.dom.VariableDeclarationStatement;
51 import org.eclipse.jdt.core.dom.rewrite.ASTRewrite;
52
53 import org.eclipse.jdt.internal.corext.codemanipulation.StubUtility;
54 import org.eclipse.jdt.internal.corext.dom.ASTNodeFactory;
55 import org.eclipse.jdt.internal.corext.dom.ASTNodes;
56 import org.eclipse.jdt.internal.corext.dom.Bindings;
57 import org.eclipse.jdt.internal.corext.dom.ScopeAnalyzer;
58 import org.eclipse.jdt.internal.corext.util.JavaModelUtil;
59
60
61 public class ConvertForLoopProposal extends LinkedCorrectionProposal {
62
63     private ForStatement fOldForStatement;
64     private EnhancedForStatement fEnhancedForStatement;
65     private AST fAst;
66     private Name fCollectionName;
67     private SingleVariableDeclaration fParameterDeclaration;
68     private ITypeBinding fOldCollectionTypeBinding;
69     private IBinding fOldCollectionBinding;
70     private IBinding fIndexBinding;
71     private boolean fCollectionIsMethodCall= false;
72     private MethodInvocation fMethodInvocation;
73
74     private static final String JavaDoc ELEMENT_KEY_REFERENCE= "element"; //$NON-NLS-1$
75

76
77     /**
78      * Visitor class for finding all references to a certain Name within the
79      * specified scope (e.g. finds all references to a local variable within the
80      * Body of a For loop).
81      */

82     private class LocalOccurencesFinder extends ASTVisitor {
83
84         private List JavaDoc fOccurences;
85         private ASTNode fScope;
86         private IBinding fTempBinding;
87         private ITypeBinding fTempTypeBinding;
88
89         /**
90          * @param collectionName The inferred name of the collection to be
91          * iterated over
92          * @param oldCollectionBinding The binding of the inferred collection
93          * @param oldCollectionTypeBinding The type binding of the inferred
94          * collection
95          * @param scope The scope of the search (i.e. the body of a For
96          * Statement
97          */

98         public LocalOccurencesFinder(Name collectionName, IBinding oldCollectionBinding, ITypeBinding oldCollectionTypeBinding,
99             ASTNode scope) {
100             this.fScope= scope;
101             fOccurences= new ArrayList JavaDoc();
102             fTempBinding= oldCollectionBinding;
103             fTempTypeBinding= oldCollectionTypeBinding;
104         }
105
106         public LocalOccurencesFinder(Name name, ASTNode scope) {
107             this.fScope= scope;
108             fOccurences= new ArrayList JavaDoc();
109             fTempBinding= name.resolveBinding();
110         }
111
112         public LocalOccurencesFinder(IBinding binding, ASTNode scope) {
113             this.fScope= scope;
114             fOccurences= new ArrayList JavaDoc();
115             fTempBinding= binding;
116         }
117
118         public void perform() {
119             fScope.accept(this);
120         }
121
122         public boolean visit(SimpleName node) {
123             if (node.getParent() instanceof VariableDeclaration) {
124                 if (((VariableDeclaration)node.getParent()).getName() == node)
125                     return true; //don't include declaration
126
}
127             if (fTempBinding != null && Bindings.equals(fTempBinding, node.resolveBinding())) {
128                 fOccurences.add(node);
129             }
130             return true;
131         }
132
133         public boolean visit(MethodInvocation methodInvocation) {
134             ArrayAccess arrayAccess= (ArrayAccess)ASTNodes.getParent(methodInvocation, ArrayAccess.class);
135             if (arrayAccess != null && fTempTypeBinding != null
136                     && Bindings.equals(fTempBinding, methodInvocation.resolveMethodBinding())) {
137                 fOccurences.add(arrayAccess);
138                 return false;
139             }
140             return true;
141         }
142
143         public List JavaDoc getOccurences() {
144             return fOccurences;
145         }
146     }
147
148     /**
149      * @param name message to be displayed in the quick fix popup
150      * @param cu CompilationUnit containing the For Statement
151      * @param forStatement The For statement to be converted
152      * @param relevance
153      * @param image
154      */

155     public ConvertForLoopProposal(String JavaDoc name, ICompilationUnit cu, ForStatement forStatement, int relevance, Image image) {
156         super(name, cu, null, relevance, image);
157         this.fOldForStatement= forStatement;
158         fAst= fOldForStatement.getAST();
159     }
160
161     /**
162      * Check if the OldFor can be converted to Enhanced For. Unless all
163      * preconditions hold true, there is no reason for this QuickAssist to pop
164      * up.
165      *
166      * @return true if all preconditions (arrayCanBeInferred &&
167      * arrayOrIndexNotAssignedTo indexNotReferencedOutsideInferredArray &&
168      * onlyOneIndexUsed && additionalTempsNotReferenced) are satisfied
169      */

170     public boolean satisfiesPreconditions() {
171         return JavaModelUtil.is50OrHigher(getCompilationUnit().getJavaProject())
172             && arrayCanBeInferred()
173             && typeBindingsAreNotNull()
174             && bodySatifiesPreconditions()
175             && initializersSatisfyPreconditions()
176             && updatersSatifyPreconditions();
177     }
178
179     private boolean typeBindingsAreNotNull() {
180         fIndexBinding= getIndexBinding();
181         return fOldCollectionBinding != null && fOldCollectionTypeBinding != null && fIndexBinding != null;
182     }
183
184     private boolean bodySatifiesPreconditions() {
185         // checks in a single pass through Loop's body that arrayOrIndexNotAssignedTo
186
// and indexNotReferencedOutsideInferredArray
187
final List JavaDoc writeAccesses= new ArrayList JavaDoc();
188         final boolean isIndexReferenced[]= {false};
189
190         fOldForStatement.getBody().accept(new ASTVisitor() {
191             public boolean visit(Assignment assignment) {
192                 classifyWriteAccess(assignment.getLeftHandSide());
193                 return true;
194             }
195             public boolean visit(PostfixExpression node) {
196                 classifyWriteAccess(node.getOperand());
197                 return true;
198             }
199             public boolean visit(PrefixExpression node) {
200                 classifyWriteAccess(node.getOperand());
201                 return true;
202             }
203             public boolean visit(SimpleName name) {
204                 IBinding binding= name.resolveBinding();
205                 if (Bindings.equals(fIndexBinding, binding)) {
206                     ASTNode parent= name.getParent();
207                     // check if the direct parent is an ArrayAcces
208
if (parent instanceof ArrayAccess){
209                         // even if the Index is referenced within an ArrayAccess
210
// it could happen that the Array is not the same as the
211
// inferred Array
212

213                         // On fixing bug https://bugs.eclipse.org/bugs/show_bug.cgi?id=73890
214
// had to treat the case when indexNotReferenced flag does not get overriden
215
// by subsequent passes through this loop
216
isIndexReferenced[0]= isIndexReferenced[0] || isAccessToADifferentArray((ArrayAccess)parent);
217                     }
218                     else {
219                         //otherwise the Index is referenced outside ArrayAccess
220
isIndexReferenced[0]= true;
221                     }
222                 }
223                 return false;
224             }
225             private void classifyWriteAccess(Expression expression) {
226                 //check that
227
if (expression instanceof ArrayAccess) {
228                     checkThatArrayIsNotAssigned(writeAccesses, expression);
229                 } else if (expression instanceof Name) {
230                     checkThatIndexIsNotAssigned(writeAccesses, expression);
231                 }
232             }
233         });
234         return writeAccesses.isEmpty() && !isIndexReferenced[0];
235     }
236
237     private void checkThatIndexIsNotAssigned(final List JavaDoc writeAccesses, Expression expression) {
238         Name name= (Name)expression;
239         IBinding binding= name.resolveBinding();
240         if (binding == fIndexBinding) {
241             writeAccesses.add(name);
242         }
243     }
244
245     private void checkThatArrayIsNotAssigned(final List JavaDoc writeAccesses, Expression expression) {
246         ArrayAccess arrayAccess= (ArrayAccess)expression;
247         if (arrayAccess.getArray() instanceof Name) {
248             Name arrayName= (Name)arrayAccess.getArray();
249             IBinding binding= arrayName.resolveBinding();
250             if (binding == fOldCollectionBinding)
251                 writeAccesses.add(arrayAccess);
252         }
253     }
254
255     private boolean isAccessToADifferentArray(ArrayAccess arrayAccess) {
256         Expression expression= arrayAccess.getArray();
257         if (expression instanceof Name) {
258             return isNameDifferentThanInferredArray((Name)expression);
259         } else if (expression instanceof FieldAccess){
260             FieldAccess fieldAccess= (FieldAccess)expression;
261             return isNameDifferentThanInferredArray(fieldAccess.getName());
262         } else if (expression instanceof MethodInvocation){
263             MethodInvocation methodCall= (MethodInvocation)expression;
264             return isNameDifferentThanInferredArray(methodCall.getName());
265         }else {
266             return true; //conservative approach: if it doesn't fall within the above cases
267
// I return that it's an access to a different Array (causing the precondition
268
// to fail)
269
}
270     }
271
272     private boolean isNameDifferentThanInferredArray(Name name) {
273         IBinding arrayBinding= name.resolveBinding();
274         if (!Bindings.equals(fOldCollectionBinding, arrayBinding)) {
275             return true;
276         }
277         return false;
278     }
279
280     private boolean updatersSatifyPreconditions() {
281         return indexNotDecremented() && onlyOneIndexUsed();
282     }
283
284     private boolean indexNotDecremented() {
285         ASTNode updater= (ASTNode)fOldForStatement.updaters().get(0);
286
287         if (updater instanceof PostfixExpression) {
288             if ("++".equals(((PostfixExpression)updater).getOperator().toString())) //$NON-NLS-1$
289
return true;
290         }
291
292         if (updater instanceof PrefixExpression){
293             if ("++".equals(((PrefixExpression)updater).getOperator().toString())) //$NON-NLS-1$
294
return true;
295         }
296         return false;
297     }
298
299     private boolean initializersSatisfyPreconditions(){
300         // Only one pass through Initializers
301
// check if startsFromZero and additionalTempsNotReferenced
302

303         final List JavaDoc tempVarsInInitializers= new ArrayList JavaDoc();
304         final boolean startsFromZero[] = {false};
305         List JavaDoc initializers= fOldForStatement.initializers();
306
307         for (Iterator JavaDoc iter = initializers.iterator(); iter.hasNext();) {
308             Expression element = (Expression) iter.next();
309             element.accept(new ASTVisitor(){
310                 public boolean visit(VariableDeclarationFragment declarationFragment){
311                     Name indexName= declarationFragment.getName();
312                     tempVarsInInitializers.add(indexName);
313                     startsFromZero[0]= doesIndexStartFromZero(indexName, declarationFragment);
314                     return false;
315                 }
316                 public boolean visit(Assignment assignment){
317                     if (assignment.getLeftHandSide() instanceof Name) {
318                         Name indexName= (Name) assignment.getLeftHandSide();
319                         tempVarsInInitializers.add(indexName);
320                         startsFromZero[0]= doesIndexStartFromZero(indexName, assignment);
321                     }
322                     return false;
323                 }
324             });
325         }
326
327         removeInferredIndexFrom(tempVarsInInitializers);
328
329         return startsFromZero[0] && additionalTempsNotReferenced(tempVarsInInitializers);
330     }
331
332     private boolean doesIndexStartFromZero(Name indexName, ASTNode declaringNode) {
333         IBinding binding= indexName.resolveBinding();
334         if (Bindings.equals(fIndexBinding, binding)){
335             Expression initializer = null;
336             if (declaringNode instanceof VariableDeclarationFragment){
337                 initializer= ((VariableDeclarationFragment)declaringNode).getInitializer();
338             } else if (declaringNode instanceof Assignment){
339                 initializer= ((Assignment) declaringNode).getRightHandSide();
340             }
341
342             if (initializer instanceof NumberLiteral){
343                 NumberLiteral number= (NumberLiteral) initializer;
344                 if (! "0".equals(number.getToken())) { //$NON-NLS-1$
345
return false;
346                 }
347             }
348         }
349         return true; // we have to return true also for the cases when we test another variable besides
350
// Inferred Index
351
}
352
353
354
355     private void removeInferredIndexFrom(List JavaDoc localTemps) {
356         Name indexName= null;
357         for (Iterator JavaDoc iter= localTemps.iterator(); iter.hasNext();) {
358             Name name= (Name)iter.next();
359             IBinding binding= name.resolveBinding();
360             //fIndexBinding has already been initialized via typeBindingsAreNotNull()
361
if (Bindings.equals(fIndexBinding, binding)) {
362                 indexName= name;
363                 break;
364             }
365         }
366         localTemps.remove(indexName);
367     }
368
369     private boolean additionalTempsNotReferenced(List JavaDoc localTemps) {
370         for (Iterator JavaDoc iter= localTemps.iterator(); iter.hasNext();) {
371             Name name= (Name)iter.next();
372             LocalOccurencesFinder finder= new LocalOccurencesFinder(name, fOldForStatement.getBody());
373             finder.perform();
374             if (!finder.getOccurences().isEmpty())
375                 return false;
376         }
377         return true;
378     }
379
380     private boolean onlyOneIndexUsed() {
381         return fOldForStatement.updaters().size() == 1;
382     }
383
384     private boolean arrayCanBeInferred() {
385         doInferCollection();
386         return (fCollectionName != null)
387             && fOldCollectionTypeBinding != null
388             // for now, only iteration over Arrays are handled
389
&& (fOldCollectionTypeBinding.isArray());
390     }
391
392     private IBinding inferIndexBinding() {
393         List JavaDoc initializers= fOldForStatement.initializers();
394         Expression expression= (Expression)initializers.get(0);
395         if (expression instanceof VariableDeclarationExpression) {
396             VariableDeclarationFragment declaration= (VariableDeclarationFragment)((VariableDeclarationExpression)expression)
397                 .fragments().get(0);
398             Name indexName= declaration.getName();
399             fIndexBinding= indexName.resolveBinding();
400         } else if (expression instanceof Assignment) {
401             Assignment assignment= (Assignment)expression;
402             Expression lhs= assignment.getLeftHandSide();
403             if (lhs instanceof Name) {
404                 Name indexName= (Name)lhs;
405                 fIndexBinding= indexName.resolveBinding();
406             }
407         }
408         return fIndexBinding;
409     }
410
411     protected ASTRewrite getRewrite() throws CoreException {
412         ASTRewrite rewrite= ASTRewrite.create(fAst);
413         doConvert(rewrite);
414         return rewrite;
415     }
416
417     private void doConvert(ASTRewrite rewrite) throws CoreException {
418         doInferCollection();
419         doInferElement();
420         doFindAndReplaceInBody(rewrite);
421
422         AST ast= fOldForStatement.getAST();
423         fEnhancedForStatement= ast.newEnhancedForStatement();
424         ASTNode theBody= rewrite.createMoveTarget(fOldForStatement.getBody());
425         fEnhancedForStatement.setBody((Statement) theBody);
426         fEnhancedForStatement.setExpression(createExpression(rewrite));
427         fEnhancedForStatement.setParameter(fParameterDeclaration);
428         addLinkedPosition(rewrite.track(fParameterDeclaration.getName()), true, ConvertForLoopProposal.ELEMENT_KEY_REFERENCE);
429
430         String JavaDoc name= fParameterDeclaration.getName().getIdentifier();
431
432         List JavaDoc proposals= getProposalsForElement();
433         if (!proposals.contains(name))
434             proposals.add(0, name);
435
436         for (Iterator JavaDoc iterator= proposals.iterator(); iterator.hasNext();)
437             addLinkedPositionProposal(ConvertForLoopProposal.ELEMENT_KEY_REFERENCE, (String JavaDoc) iterator.next(), null);
438
439         rewrite.replace(fOldForStatement, fEnhancedForStatement, null);
440     }
441
442     private Expression createExpression(ASTRewrite rewrite) {
443         if (fCollectionIsMethodCall) {
444             MethodInvocation methodCall= (MethodInvocation) rewrite.createMoveTarget(fMethodInvocation);
445             return methodCall;
446         } else
447             return fCollectionName;
448     }
449
450     private List JavaDoc getProposalsForElement() {
451         List JavaDoc list= new ArrayList JavaDoc();
452         ICompilationUnit icu= getCompilationUnit();
453         IJavaProject javaProject= icu.getJavaProject();
454         int dimensions= fOldCollectionTypeBinding.getDimensions() - 1;
455         final List JavaDoc used= getUsedVariableNames();
456         String JavaDoc type= fOldCollectionTypeBinding.getName();
457         if (fOldCollectionTypeBinding.isArray())
458             type= fOldCollectionTypeBinding.getElementType().getName();
459         String JavaDoc[] proposals= StubUtility.getLocalNameSuggestions(javaProject, type, dimensions, (String JavaDoc[]) used.toArray(new String JavaDoc[used.size()]));
460         for (int i= 0; i < proposals.length; i++) {
461             list.add(proposals[i]);
462         }
463         return list;
464     }
465
466     private List JavaDoc getUsedVariableNames() {
467         CompilationUnit root= (CompilationUnit)fOldForStatement.getRoot();
468         IBinding[] varsBefore= (new ScopeAnalyzer(root)).getDeclarationsInScope(fOldForStatement.getStartPosition(),
469             ScopeAnalyzer.VARIABLES);
470         IBinding[] varsAfter= (new ScopeAnalyzer(root)).getDeclarationsAfter(fOldForStatement.getStartPosition()
471             + fOldForStatement.getLength(), ScopeAnalyzer.VARIABLES);
472
473         List JavaDoc names= new ArrayList JavaDoc();
474         for (int i= 0; i < varsBefore.length; i++) {
475             names.add(varsBefore[i].getName());
476         }
477         for (int i= 0; i < varsAfter.length; i++) {
478             names.add(varsAfter[i].getName());
479         }
480         return names;
481     }
482
483     private void doFindAndReplaceInBody(ASTRewrite rewrite) {
484         LocalOccurencesFinder finder= new LocalOccurencesFinder(fCollectionName, fOldCollectionBinding,
485             fOldCollectionTypeBinding, fOldForStatement.getBody());
486         finder.perform();
487         List JavaDoc occurences= finder.getOccurences();
488
489         // this might be the "ideal" case (exercised in testNiceReduction)
490
if (occurences.size() == 1) {
491             ASTNode soleOccurence= (ASTNode)occurences.get(0);
492             ArrayAccess arrayAccess= soleOccurence instanceof ArrayAccess
493                 ? (ArrayAccess)soleOccurence
494                 : (ArrayAccess)ASTNodes.getParent(soleOccurence, ArrayAccess.class);
495             if (arrayAccess != null) {
496                 if (arrayAccess.getParent() instanceof VariableDeclarationFragment) {
497                     replaceSingleVariableDeclaration(rewrite, arrayAccess);
498                     return;
499                 }
500             }
501         }
502
503         replaceMultipleOccurences(rewrite, occurences);
504     }
505
506     private void replaceSingleVariableDeclaration(ASTRewrite rewrite, ArrayAccess arrayAccess) {
507         VariableDeclarationFragment declarationFragment= (VariableDeclarationFragment)arrayAccess.getParent();
508         VariableDeclarationStatement declarationStatement= (VariableDeclarationStatement)declarationFragment.getParent();
509
510         // if could not infer THE_ELEMENT from infer step, we might
511
// be able to infer it from here
512
if (fParameterDeclaration == null) {
513             fParameterDeclaration= fAst.newSingleVariableDeclaration();
514         }
515
516         SimpleName theTempVariable= declarationFragment.getName();
517
518         SimpleName name= fAst.newSimpleName(theTempVariable.getIdentifier());
519         Type type= ASTNodeFactory.newType(getAst(), declarationFragment);
520         fParameterDeclaration.setName(name);
521         fParameterDeclaration.setType(type);
522
523         LocalOccurencesFinder finder2= new LocalOccurencesFinder(theTempVariable.resolveBinding(), fOldForStatement.getBody());
524         finder2.perform();
525         List JavaDoc occurences2= finder2.getOccurences();
526
527         linkAllReferences(rewrite, occurences2);
528
529         rewrite.replace(declarationStatement, null, null);
530         return;
531     }
532
533     private void linkAllReferences(ASTRewrite rewrite, List JavaDoc occurences) {
534         for (Iterator JavaDoc iter= occurences.iterator(); iter.hasNext();) {
535             ASTNode variableRef= (ASTNode)iter.next();
536             addLinkedPosition(rewrite.track(variableRef), false, ELEMENT_KEY_REFERENCE);
537         }
538     }
539
540     private void replaceMultipleOccurences(ASTRewrite rewrite, List JavaDoc occurences) {
541         for (Iterator JavaDoc iter= occurences.iterator(); iter.hasNext();) {
542             ASTNode element= (ASTNode)iter.next();
543             ArrayAccess arrayAccess= element instanceof ArrayAccess ? (ArrayAccess)element : (ArrayAccess)ASTNodes.getParent(
544                 element, ArrayAccess.class);
545             if (arrayAccess != null) {
546                 SimpleName elementReference= fAst.newSimpleName(fParameterDeclaration.getName().getIdentifier());
547
548                 rewrite.replace(arrayAccess, elementReference, null);
549                 addLinkedPosition(rewrite.track(elementReference), false, ELEMENT_KEY_REFERENCE);
550
551             }
552         }
553     }
554
555     private void doInferElement() throws CoreException {
556         if (fCollectionName == null) {
557             createDefaultParameter();
558         } else {
559             if (fOldCollectionTypeBinding.isArray()) {
560                 ITypeBinding elementType= fOldCollectionTypeBinding.getElementType();
561                 fParameterDeclaration= fAst.newSingleVariableDeclaration();
562                 SimpleName name= fAst.newSimpleName(ELEMENT_KEY_REFERENCE);
563                 fParameterDeclaration.setName(name);
564                 Type theType= getImportRewrite().addImport(elementType, fAst);
565                 if (fOldCollectionTypeBinding.getDimensions() != 1) {
566                     theType= fAst.newArrayType(theType, fOldCollectionTypeBinding.getDimensions() - 1);
567                 }
568                 fParameterDeclaration.setType(theType);
569             }
570         }
571     }
572
573     private void createDefaultParameter() {
574         fParameterDeclaration= fAst.newSingleVariableDeclaration();
575         SimpleName name= fAst.newSimpleName(ELEMENT_KEY_REFERENCE);
576         Type type= fAst.newPrimitiveType(PrimitiveType.INT);
577         fParameterDeclaration.setName(name);
578         fParameterDeclaration.setType(type);
579     }
580
581     // Caches the inferred collection name and its bindings in local fields. These
582
// won't change during the whole operation of the QuickFix.
583
private void doInferCollection() {
584         if (fCollectionName != null)
585             return;
586
587         doInferCollectionFromExpression();
588
589         if (fCollectionName == null)
590             doInferCollectionFromInitializers();
591
592     }
593
594     private void doInferCollectionFromExpression() {
595         Expression stopCondition= fOldForStatement.getExpression();
596         if (stopCondition.getNodeType() == ASTNode.INFIX_EXPRESSION) {
597             Expression rightOperand= ((InfixExpression)stopCondition).getRightOperand();
598             if (rightOperand.getNodeType() == ASTNode.QUALIFIED_NAME) {
599                 Name qualifier= ((QualifiedName)rightOperand).getQualifier();
600                 fCollectionName= ASTNodeFactory.newName(fAst,qualifier.getFullyQualifiedName());
601                 fOldCollectionBinding= qualifier.resolveBinding();
602                 fOldCollectionTypeBinding= qualifier.resolveTypeBinding();
603             } else if (rightOperand.getNodeType() == ASTNode.METHOD_INVOCATION) {
604                 MethodInvocation methodCall= (MethodInvocation)rightOperand;
605                 Expression exp= methodCall.getExpression();
606                 if (exp instanceof Name) {
607                     Name collectionName= (Name)exp;
608                     fOldCollectionBinding= collectionName.resolveBinding();
609                     fOldCollectionTypeBinding= collectionName.resolveTypeBinding();
610                     fCollectionName= ASTNodeFactory.newName(fAst,collectionName.getFullyQualifiedName());
611                 }
612             } else if (rightOperand instanceof FieldAccess){
613                 // this treats the case when the stop condition is a method call
614
// which returns an Array on which the "length" field is queried
615
FieldAccess fieldAccess= (FieldAccess) rightOperand;
616                 if ("length".equals(fieldAccess.getName().getIdentifier())) { //$NON-NLS-1$
617
fCollectionIsMethodCall= true;
618                     if (fieldAccess.getExpression() instanceof MethodInvocation){
619                         MethodInvocation methodCall= (MethodInvocation) fieldAccess.getExpression();
620                         fMethodInvocation= methodCall;
621                         fOldCollectionBinding= methodCall.resolveMethodBinding();
622                         fOldCollectionTypeBinding= methodCall.resolveTypeBinding();
623                         fCollectionName= ASTNodeFactory.newName(fAst, methodCall.getName().getFullyQualifiedName());
624                     }
625                 }
626
627             }
628         }
629     }
630
631     private void doInferCollectionFromInitializers() {
632         List JavaDoc initializers= fOldForStatement.initializers();
633         for (Iterator JavaDoc iter= initializers.iterator(); iter.hasNext();) {
634             VariableDeclarationExpression element= (VariableDeclarationExpression)iter.next();
635             List JavaDoc declarationFragments= element.fragments();
636             for (Iterator JavaDoc iterator= declarationFragments.iterator(); iterator.hasNext();) {
637                 VariableDeclarationFragment fragment= (VariableDeclarationFragment)iterator.next();
638                 doInferCollectionFromFragment(fragment);
639             }
640         }
641     }
642
643     /**
644      * @param fragment VariableDeclarationFragment to visit. This helper method is useful
645      * for the IDIOM when the stop condition is expressed with another
646      * variable within loop: for (int i=0, max= array.length; i < max;
647      * i++){}
648      */

649     private void doInferCollectionFromFragment(VariableDeclarationFragment fragment) {
650         Expression initializer= fragment.getInitializer();
651         initializer.accept(new ASTVisitor() {
652             public boolean visit(QualifiedName qualifiedName) {
653                 initializeBindings(qualifiedName.getQualifier());
654                 return false;
655             }
656             public boolean visit(SimpleName simpleName) {
657                 initializeBindings(simpleName);
658                 return false;
659             }
660             public boolean visit(MethodInvocation methodCall){
661                 ITypeBinding typeBinding= methodCall.resolveTypeBinding();
662                 if (typeBinding.isArray()){
663                     fCollectionIsMethodCall= true;
664                     fMethodInvocation= methodCall;
665                     fOldCollectionTypeBinding= typeBinding;
666                     fOldCollectionBinding= methodCall.resolveMethodBinding();
667                     fCollectionName= ASTNodeFactory.newName(fAst, methodCall.getName().getFullyQualifiedName());
668                 }
669                 return false;
670             }
671             private void initializeBindings(Name name) {
672                 ITypeBinding typeBinding= name.resolveTypeBinding();
673                 if (typeBinding != null && typeBinding.isArray()) {
674                     fOldCollectionTypeBinding= typeBinding;
675                     fOldCollectionBinding= name.resolveBinding();
676                     fCollectionName= ASTNodeFactory.newName(fAst,name.getFullyQualifiedName());
677                 }
678             }
679         });
680     }
681
682     private AST getAst() {
683         return fAst;
684     }
685
686     // lazy load. Caches the binding of the For's index in a field since it cannot
687
// be change during the whole QuickFix
688
private IBinding getIndexBinding(){
689         if (fIndexBinding != null)
690             return fIndexBinding;
691         else return inferIndexBinding();
692     }
693 }
694
Popular Tags