KickJava   Java API By Example, From Geeks To Geeks.

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


1 /*******************************************************************************
2  * Copyright (c) 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.Assignment;
27 import org.eclipse.jdt.core.dom.Block;
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.IMethodBinding;
35 import org.eclipse.jdt.core.dom.ITypeBinding;
36 import org.eclipse.jdt.core.dom.IVariableBinding;
37 import org.eclipse.jdt.core.dom.MethodInvocation;
38 import org.eclipse.jdt.core.dom.Name;
39 import org.eclipse.jdt.core.dom.NullLiteral;
40 import org.eclipse.jdt.core.dom.SimpleName;
41 import org.eclipse.jdt.core.dom.SingleVariableDeclaration;
42 import org.eclipse.jdt.core.dom.Statement;
43 import org.eclipse.jdt.core.dom.VariableDeclarationExpression;
44 import org.eclipse.jdt.core.dom.VariableDeclarationFragment;
45 import org.eclipse.jdt.core.dom.rewrite.ASTRewrite;
46 import org.eclipse.jdt.core.dom.rewrite.ListRewrite;
47
48 import org.eclipse.jdt.internal.corext.codemanipulation.StubUtility;
49 import org.eclipse.jdt.internal.corext.dom.ASTNodes;
50 import org.eclipse.jdt.internal.corext.dom.ScopeAnalyzer;
51 import org.eclipse.jdt.internal.corext.util.JavaModelUtil;
52
53 /**
54  * Correction proposal to convert for loops over iterables to enhanced for loops.
55  *
56  * @since 3.1
57  */

58 public final class ConvertIterableLoopProposal extends LinkedCorrectionProposal {
59
60     /** The linked position group for the element variable */
61     private static final String JavaDoc GROUP_ELEMENT= "element"; //$NON-NLS-1$
62

63     /**
64      * Returns the supertype of the given type with the qualified name.
65      *
66      * @param binding the binding of the type
67      * @param name the qualified name of the supertype
68      * @return the supertype, or <code>null</code>
69      */

70     private static ITypeBinding getSuperType(final ITypeBinding binding, final String JavaDoc name) {
71
72         if (binding.isArray() || binding.isPrimitive())
73             return null;
74
75         if (binding.getQualifiedName().startsWith(name))
76             return binding;
77
78         final ITypeBinding type= binding.getSuperclass();
79         if (type != null) {
80             final ITypeBinding result= getSuperType(type, name);
81             if (result != null)
82                 return result;
83         }
84         final ITypeBinding[] types= binding.getInterfaces();
85         for (int index= 0; index < types.length; index++) {
86             final ITypeBinding result= getSuperType(types[index], name);
87             if (result != null)
88                 return result;
89         }
90         return null;
91     }
92
93     /** Has the element variable been assigned outside the for statement? */
94     private boolean fAssigned= false;
95
96     /** The ast to operate on */
97     private final AST fAst;
98
99     /** The binding of the element variable */
100     private IBinding fElement= null;
101
102     /** The node of the iterable object used in the expression */
103     private Expression fExpression= null;
104
105     /** The binding of the iterable object */
106     private IBinding fIterable= null;
107
108     /** The binding of the iterator variable */
109     private IVariableBinding fIterator= null;
110
111     /** The nodes of the element variable occurrences */
112     private List JavaDoc fOccurrences= new ArrayList JavaDoc(2);
113
114     /** The for statement to convert */
115     private final ForStatement fStatement;
116
117     /**
118      * Creates a new convert iterable loop proposal.
119      *
120      * @param name the name of the correction proposal
121      * @param unit the compilation unit containing the for statement
122      * @param statement the for statement to be converted
123      * @param relevance the relevance of the proposal
124      * @param image the image of the proposal
125      */

126     public ConvertIterableLoopProposal(final String JavaDoc name, final ICompilationUnit unit, final ForStatement statement, final int relevance, final Image image) {
127         super(name, unit, null, relevance, image);
128         fStatement= statement;
129         fAst= statement.getAST();
130     }
131
132     private List JavaDoc computeElementNames() {
133         final List JavaDoc names= new ArrayList JavaDoc();
134         final IJavaProject project= getCompilationUnit().getJavaProject();
135         String JavaDoc name= GROUP_ELEMENT;
136         final ITypeBinding binding= fIterator.getType();
137         if (binding != null && binding.isParameterizedType())
138             name= binding.getTypeArguments()[0].getName();
139         final List JavaDoc excluded= getExcludedNames();
140         final String JavaDoc[] suggestions= StubUtility.getLocalNameSuggestions(project, name, 0, (String JavaDoc[]) excluded.toArray(new String JavaDoc[excluded.size()]));
141         for (int index= 0; index < suggestions.length; index++)
142             names.add(suggestions[index]);
143         return names;
144     }
145
146     private List JavaDoc getExcludedNames() {
147         final CompilationUnit unit= (CompilationUnit) fStatement.getRoot();
148         final IBinding[] before= (new ScopeAnalyzer(unit)).getDeclarationsInScope(fStatement.getStartPosition(), ScopeAnalyzer.VARIABLES);
149         final IBinding[] after= (new ScopeAnalyzer(unit)).getDeclarationsAfter(fStatement.getStartPosition() + fStatement.getLength(), ScopeAnalyzer.VARIABLES);
150         final List JavaDoc names= new ArrayList JavaDoc();
151         for (int index= 0; index < before.length; index++)
152             names.add(before[index].getName());
153         for (int index= 0; index < after.length; index++)
154             names.add(after[index].getName());
155         return names;
156     }
157
158     /**
159      * Returns the expression for the enhanced for statement.
160      *
161      * @param rewrite the ast rewrite to use
162      * @return the expression node
163      */

164     private Expression getExpression(final ASTRewrite rewrite) {
165         if (fExpression instanceof MethodInvocation)
166             return (MethodInvocation) rewrite.createMoveTarget(fExpression);
167         return (Expression) ASTNode.copySubtree(rewrite.getAST(), fExpression);
168     }
169
170     /**
171      * Returns the iterable type from the iterator type binding.
172      *
173      * @param iterator the iterator type binding, or <code>null</code>
174      * @return the iterable type
175      */

176     private ITypeBinding getIterableType(final ITypeBinding iterator) {
177         if (iterator != null) {
178             final ITypeBinding[] bindings= iterator.getTypeArguments();
179             if (bindings.length > 0)
180                 return bindings[0];
181         }
182         return fAst.resolveWellKnownType("java.lang.Object"); //$NON-NLS-1$
183
}
184
185     /*
186      * @see org.eclipse.jdt.internal.ui.text.correction.ASTRewriteCorrectionProposal#getRewrite()
187      */

188     protected final ASTRewrite getRewrite() throws CoreException {
189         final ASTRewrite rewrite= ASTRewrite.create(fAst);
190         final EnhancedForStatement statement= fAst.newEnhancedForStatement();
191         final List JavaDoc names= computeElementNames();
192         String JavaDoc name= GROUP_ELEMENT;
193         if (fElement != null) {
194             name= fElement.getName();
195             if (!names.contains(name))
196                 names.add(0, name);
197         } else {
198             if (!names.isEmpty())
199                 name= (String JavaDoc) names.get(0);
200         }
201         for (final Iterator JavaDoc iterator= names.iterator(); iterator.hasNext();)
202             addLinkedPositionProposal(GROUP_ELEMENT, (String JavaDoc) iterator.next(), null);
203         final Statement body= fStatement.getBody();
204         if (body != null) {
205             if (body instanceof Block) {
206                 final ListRewrite list= rewrite.getListRewrite(body, Block.STATEMENTS_PROPERTY);
207                 for (final Iterator JavaDoc iterator= fOccurrences.iterator(); iterator.hasNext();) {
208                     final Statement parent= (Statement) ASTNodes.getParent((ASTNode) iterator.next(), Statement.class);
209                     if (parent != null && list.getRewrittenList().contains(parent))
210                         list.remove(parent, null);
211                 }
212                 final String JavaDoc text= name;
213                 body.accept(new ASTVisitor() {
214
215                     private boolean replace(final Expression expression) {
216                         final SimpleName node= fAst.newSimpleName(text);
217                         rewrite.replace(expression, node, null);
218                         addLinkedPosition(rewrite.track(node), false, GROUP_ELEMENT);
219                         return false;
220                     }
221
222                     public final boolean visit(final MethodInvocation node) {
223                         final IMethodBinding binding= node.resolveMethodBinding();
224                         if (binding != null && binding.getName().equals("next")) { //$NON-NLS-1$
225
final Expression expression= node.getExpression();
226                             if (expression instanceof Name) {
227                                 final IBinding result= ((Name) expression).resolveBinding();
228                                 if (result != null && result.equals(fIterator))
229                                     return replace(node);
230                             } else if (expression instanceof MethodInvocation) {
231                                 final IBinding result= ((MethodInvocation) expression).resolveMethodBinding();
232                                 if (result != null && result.equals(fIterator))
233                                     return replace(node);
234                             } else if (expression instanceof FieldAccess) {
235                                 final IBinding result= ((FieldAccess) expression).resolveFieldBinding();
236                                 if (result != null && result.equals(fIterator))
237                                     return replace(node);
238                             }
239                         }
240                         return super.visit(node);
241                     }
242
243                     public final boolean visit(final SimpleName node) {
244                         if (fElement != null) {
245                             final IBinding binding= node.resolveBinding();
246                             if (binding != null && binding.equals(fElement)) {
247                                 final Statement parent= (Statement) ASTNodes.getParent(node, Statement.class);
248                                 if (parent != null && list.getRewrittenList().contains(parent))
249                                     addLinkedPosition(rewrite.track(node), false, GROUP_ELEMENT);
250                             }
251                         }
252                         return false;
253                     }
254                 });
255             }
256             statement.setBody((Statement) rewrite.createMoveTarget(body));
257         }
258         final SingleVariableDeclaration declaration= fAst.newSingleVariableDeclaration();
259         final SimpleName simple= fAst.newSimpleName(name);
260         addLinkedPosition(rewrite.track(simple), true, GROUP_ELEMENT);
261         declaration.setName(simple);
262         declaration.setType(getImportRewrite().addImport(getIterableType(fIterator.getType()), fAst));
263         statement.setParameter(declaration);
264         statement.setExpression(getExpression(rewrite));
265         rewrite.replace(fStatement, statement, null);
266         return rewrite;
267     }
268
269     /**
270      * Is this proposal applicable?
271      *
272      * @return <code>true</code> if it is applicable, <code>false</code> otherwise
273      */

274     public final boolean isApplicable() {
275         if (JavaModelUtil.is50OrHigher(getCompilationUnit().getJavaProject())) {
276             for (final Iterator JavaDoc outer= fStatement.initializers().iterator(); outer.hasNext();) {
277                 final Expression initializer= (Expression) outer.next();
278                 if (initializer instanceof VariableDeclarationExpression) {
279                     final VariableDeclarationExpression declaration= (VariableDeclarationExpression) initializer;
280                     for (Iterator JavaDoc inner= declaration.fragments().iterator(); inner.hasNext();) {
281                         final VariableDeclarationFragment fragment= (VariableDeclarationFragment) inner.next();
282                         fragment.accept(new ASTVisitor() {
283
284                             public final boolean visit(final MethodInvocation node) {
285                                 final IMethodBinding binding= node.resolveMethodBinding();
286                                 if (binding != null && binding.getName().equals("iterator")) { //$NON-NLS-1$
287
final Expression qualifier= node.getExpression();
288                                     if (qualifier != null) {
289                                         final ITypeBinding type= qualifier.resolveTypeBinding();
290                                         if (type != null) {
291                                             final ITypeBinding iterable= getSuperType(type, "java.lang.Iterable"); //$NON-NLS-1$
292
if (iterable != null) {
293                                                 fExpression= qualifier;
294                                                 if (qualifier instanceof Name) {
295                                                     final Name name= (Name) qualifier;
296                                                     fIterable= name.resolveBinding();
297                                                 } else if (qualifier instanceof MethodInvocation) {
298                                                     final MethodInvocation invocation= (MethodInvocation) qualifier;
299                                                     fIterable= invocation.resolveMethodBinding();
300                                                 } else if (qualifier instanceof FieldAccess) {
301                                                     final FieldAccess access= (FieldAccess) qualifier;
302                                                     fIterable= access.resolveFieldBinding();
303                                                 }
304                                             }
305                                         }
306                                     }
307                                 }
308                                 return true;
309                             }
310
311                             public final boolean visit(final VariableDeclarationFragment node) {
312                                 final IVariableBinding binding= node.resolveBinding();
313                                 if (binding != null) {
314                                     final ITypeBinding type= binding.getType();
315                                     if (type != null) {
316                                         final ITypeBinding iterator= getSuperType(type, "java.util.Iterator"); //$NON-NLS-1$
317
if (iterator != null)
318                                             fIterator= binding;
319                                     }
320                                 }
321                                 return true;
322                             }
323                         });
324                     }
325                 }
326             }
327             final Statement statement= fStatement.getBody();
328             if (statement != null && fIterator != null) {
329                 final ITypeBinding iterable= getIterableType(fIterator.getType());
330                 statement.accept(new ASTVisitor() {
331
332                     public final boolean visit(final Assignment node) {
333                         return visit(node.getLeftHandSide(), node.getRightHandSide());
334                     }
335
336                     private boolean visit(final Expression node) {
337                         if (node != null) {
338                             final ITypeBinding binding= node.resolveTypeBinding();
339                             if (binding != null && iterable.equals(binding)) {
340                                 if (node instanceof Name) {
341                                     final Name name= (Name) node;
342                                     final IBinding result= name.resolveBinding();
343                                     if (result != null) {
344                                         fOccurrences.add(node);
345                                         fElement= result;
346                                         return false;
347                                     }
348                                 } else if (node instanceof FieldAccess) {
349                                     final FieldAccess access= (FieldAccess) node;
350                                     final IBinding result= access.resolveFieldBinding();
351                                     if (result != null) {
352                                         fOccurrences.add(node);
353                                         fElement= result;
354                                         return false;
355                                     }
356                                 }
357                             }
358                         }
359                         return true;
360                     }
361
362                     private boolean visit(final Expression left, final Expression right) {
363                         if (right instanceof MethodInvocation) {
364                             final MethodInvocation invocation= (MethodInvocation) right;
365                             final IMethodBinding binding= invocation.resolveMethodBinding();
366                             if (binding != null && binding.getName().equals("next")) { //$NON-NLS-1$
367
final Expression expression= invocation.getExpression();
368                                 if (expression instanceof Name) {
369                                     final Name qualifier= (Name) expression;
370                                     final IBinding result= qualifier.resolveBinding();
371                                     if (result != null && result.equals(fIterator))
372                                         return visit(left);
373                                 } else if (expression instanceof MethodInvocation) {
374                                     final MethodInvocation qualifier= (MethodInvocation) expression;
375                                     final IBinding result= qualifier.resolveMethodBinding();
376                                     if (result != null && result.equals(fIterator))
377                                         return visit(left);
378                                 } else if (expression instanceof FieldAccess) {
379                                     final FieldAccess qualifier= (FieldAccess) expression;
380                                     final IBinding result= qualifier.resolveFieldBinding();
381                                     if (result != null && result.equals(fIterator))
382                                         return visit(left);
383                                 }
384                             }
385                         } else if (right instanceof NullLiteral)
386                             return visit(left);
387                         return true;
388                     }
389
390                     public final boolean visit(final VariableDeclarationFragment node) {
391                         return visit(node.getName(), node.getInitializer());
392                     }
393                 });
394             }
395             final ASTNode root= fStatement.getRoot();
396             if (root != null) {
397                 root.accept(new ASTVisitor() {
398
399                     public final boolean visit(final ForStatement node) {
400                         return false;
401                     }
402
403                     public final boolean visit(final SimpleName node) {
404                         final IBinding binding= node.resolveBinding();
405                         if (binding != null && binding.equals(fElement))
406                             fAssigned= true;
407                         return false;
408                     }
409                 });
410             }
411         }
412         return fExpression != null && fIterable != null && fIterator != null && !fAssigned;
413     }
414 }
Popular Tags