1 11 package org.eclipse.jdt.internal.corext.fix; 12 13 import java.util.ArrayList ; 14 import java.util.Iterator ; 15 import java.util.List ; 16 17 import org.eclipse.text.edits.TextEditGroup; 18 19 import org.eclipse.core.runtime.CoreException; 20 import org.eclipse.core.runtime.IStatus; 21 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.Modifier; 39 import org.eclipse.jdt.core.dom.Name; 40 import org.eclipse.jdt.core.dom.NullLiteral; 41 import org.eclipse.jdt.core.dom.SimpleName; 42 import org.eclipse.jdt.core.dom.SingleVariableDeclaration; 43 import org.eclipse.jdt.core.dom.Statement; 44 import org.eclipse.jdt.core.dom.ThisExpression; 45 import org.eclipse.jdt.core.dom.VariableDeclarationExpression; 46 import org.eclipse.jdt.core.dom.VariableDeclarationFragment; 47 import org.eclipse.jdt.core.dom.rewrite.ASTRewrite; 48 import org.eclipse.jdt.core.dom.rewrite.ImportRewrite; 49 import org.eclipse.jdt.core.dom.rewrite.ListRewrite; 50 51 import org.eclipse.jdt.internal.corext.codemanipulation.StubUtility; 52 import org.eclipse.jdt.internal.corext.dom.ASTNodes; 53 import org.eclipse.jdt.internal.corext.dom.ModifierRewrite; 54 import org.eclipse.jdt.internal.corext.refactoring.structure.CompilationUnitRewrite; 55 import org.eclipse.jdt.internal.corext.refactoring.structure.ImportRemover; 56 import org.eclipse.jdt.internal.corext.refactoring.util.TightSourceRangeComputer; 57 import org.eclipse.jdt.internal.corext.util.JavaModelUtil; 58 import org.eclipse.jdt.internal.corext.util.Messages; 59 60 import org.eclipse.jdt.internal.ui.dialogs.StatusInfo; 61 import org.eclipse.jdt.internal.ui.text.correction.ASTResolving; 62 63 68 public final class ConvertIterableLoopOperation extends ConvertLoopOperation { 69 70 79 private static ITypeBinding getSuperType(final ITypeBinding binding, final String name) { 80 81 if (binding.isArray() || binding.isPrimitive()) 82 return null; 83 84 if (binding.getQualifiedName().startsWith(name)) 85 return binding; 86 87 final ITypeBinding type= binding.getSuperclass(); 88 if (type != null) { 89 final ITypeBinding result= getSuperType(type, name); 90 if (result != null) 91 return result; 92 } 93 final ITypeBinding[] types= binding.getInterfaces(); 94 for (int index= 0; index < types.length; index++) { 95 final ITypeBinding result= getSuperType(types[index], name); 96 if (result != null) 97 return result; 98 } 99 return null; 100 } 101 102 103 private boolean fAssigned= false; 104 105 106 private IBinding fElement= null; 107 108 109 private Expression fExpression= null; 110 111 112 private IBinding fIterable= null; 113 114 115 private boolean fThis= false; 116 117 118 private IVariableBinding fIterator= null; 119 120 121 private final List fOccurrences= new ArrayList (2); 122 123 private EnhancedForStatement fEnhancedForLoop; 124 125 private final boolean fMakeFinal; 126 127 public ConvertIterableLoopOperation(ForStatement statement) { 128 this(statement, new String [0], false); 129 } 130 131 public ConvertIterableLoopOperation(ForStatement statement, String [] usedNames, boolean makeFinal) { 132 super(statement, usedNames); 133 fMakeFinal= makeFinal; 134 } 135 136 public String getIntroducedVariableName() { 137 if (fElement != null) { 138 return fElement.getName(); 139 } else { 140 return getVariableNameProposals()[0]; 141 } 142 } 143 144 private String [] getVariableNameProposals() { 145 146 String [] variableNames= getUsedVariableNames(); 147 String [] elementSuggestions= StubUtility.getLocalNameSuggestions(getJavaProject(), FOR_LOOP_ELEMENT_IDENTIFIER, 0, variableNames); 148 149 final ITypeBinding binding= fIterator.getType(); 150 if (binding != null && binding.isParameterizedType()) { 151 String type= binding.getTypeArguments()[0].getName(); 152 String [] typeSuggestions= StubUtility.getLocalNameSuggestions(getJavaProject(), type, 0, variableNames); 153 154 String [] result= new String [elementSuggestions.length + typeSuggestions.length]; 155 System.arraycopy(typeSuggestions, 0, result, 0, typeSuggestions.length); 156 System.arraycopy(elementSuggestions, 0, result, typeSuggestions.length, elementSuggestions.length); 157 return result; 158 } else { 159 return elementSuggestions; 160 } 161 } 162 163 private IJavaProject getJavaProject() { 164 return getRoot().getJavaElement().getJavaProject(); 165 } 166 167 private CompilationUnit getRoot() { 168 return (CompilationUnit)getForStatement().getRoot(); 169 } 170 171 178 private Expression getExpression(final ASTRewrite rewrite) { 179 if (fThis) 180 return rewrite.getAST().newThisExpression(); 181 if (fExpression instanceof MethodInvocation) 182 return (MethodInvocation)rewrite.createMoveTarget(fExpression); 183 return (Expression)ASTNode.copySubtree(rewrite.getAST(), fExpression); 184 } 185 186 193 private ITypeBinding getIterableType(final ITypeBinding iterator) { 194 if (iterator != null) { 195 final ITypeBinding[] bindings= iterator.getTypeArguments(); 196 if (bindings.length > 0) { 197 ITypeBinding arg= bindings[0]; 198 if (arg.isWildcardType()) { 199 arg= ASTResolving.normalizeWildcardType(arg, true, getRoot().getAST()); 200 } 201 return arg; 202 } 203 } 204 return getRoot().getAST().resolveWellKnownType("java.lang.Object"); } 206 207 210 public void rewriteAST(CompilationUnitRewrite cuRewrite, List textEditGroups, final LinkedProposalModel positionGroups) throws CoreException { 211 final TextEditGroup group= createTextEditGroup(FixMessages.Java50Fix_ConvertToEnhancedForLoop_description); 212 textEditGroups.add(group); 213 214 final ASTRewrite astRewrite= cuRewrite.getASTRewrite(); 215 216 TightSourceRangeComputer rangeComputer; 217 if (astRewrite.getExtendedSourceRangeComputer() instanceof TightSourceRangeComputer) { 218 rangeComputer= (TightSourceRangeComputer)astRewrite.getExtendedSourceRangeComputer(); 219 } else { 220 rangeComputer= new TightSourceRangeComputer(); 221 } 222 rangeComputer.addTightSourceNode(getForStatement()); 223 astRewrite.setTargetSourceRangeComputer(rangeComputer); 224 225 Statement statement= convert(cuRewrite, group, positionGroups); 226 astRewrite.replace(getForStatement(), statement, group); 227 } 228 229 protected Statement convert(CompilationUnitRewrite cuRewrite, final TextEditGroup group, final LinkedProposalModel positionGroups) throws CoreException { 230 final AST ast= cuRewrite.getAST(); 231 final ASTRewrite astRewrite= cuRewrite.getASTRewrite(); 232 final ImportRewrite importRewrite= cuRewrite.getImportRewrite(); 233 final ImportRemover remover= cuRewrite.getImportRemover(); 234 235 fEnhancedForLoop= ast.newEnhancedForStatement(); 236 String [] names= getVariableNameProposals(); 237 238 String name; 239 if (fElement != null) { 240 name= fElement.getName(); 241 } else { 242 name= names[0]; 243 } 244 final LinkedProposalPositionGroup pg= positionGroups.getPositionGroup(name, true); 245 if (fElement != null) 246 pg.addProposal(name, null, 10); 247 for (int i= 0; i < names.length; i++) { 248 pg.addProposal(names[i], null, 10); 249 } 250 251 final Statement body= getForStatement().getBody(); 252 if (body != null) { 253 final ListRewrite list; 254 if (body instanceof Block) { 255 list= astRewrite.getListRewrite(body, Block.STATEMENTS_PROPERTY); 256 for (final Iterator iterator= fOccurrences.iterator(); iterator.hasNext();) { 257 final Statement parent= (Statement)ASTNodes.getParent((ASTNode)iterator.next(), Statement.class); 258 if (parent != null && list.getRewrittenList().contains(parent)) { 259 list.remove(parent, null); 260 remover.registerRemovedNode(parent); 261 } 262 } 263 } else { 264 list= null; 265 } 266 final String text= name; 267 body.accept(new ASTVisitor() { 268 269 private boolean replace(final Expression expression) { 270 final SimpleName node= ast.newSimpleName(text); 271 astRewrite.replace(expression, node, group); 272 remover.registerRemovedNode(expression); 273 pg.addPosition(astRewrite.track(node), false); 274 return false; 275 } 276 277 public final boolean visit(final MethodInvocation node) { 278 final IMethodBinding binding= node.resolveMethodBinding(); 279 if (binding != null && (binding.getName().equals("next") || binding.getName().equals("nextElement"))) { final Expression expression= node.getExpression(); 281 if (expression instanceof Name) { 282 final IBinding result= ((Name)expression).resolveBinding(); 283 if (result != null && result.equals(fIterator)) 284 return replace(node); 285 } else if (expression instanceof FieldAccess) { 286 final IBinding result= ((FieldAccess)expression).resolveFieldBinding(); 287 if (result != null && result.equals(fIterator)) 288 return replace(node); 289 } 290 } 291 return super.visit(node); 292 } 293 294 public final boolean visit(final SimpleName node) { 295 if (fElement != null) { 296 final IBinding binding= node.resolveBinding(); 297 if (binding != null && binding.equals(fElement)) { 298 final Statement parent= (Statement)ASTNodes.getParent(node, Statement.class); 299 if (parent != null && (list == null || list.getRewrittenList().contains(parent))) 300 pg.addPosition(astRewrite.track(node), false); 301 } 302 } 303 return false; 304 } 305 }); 306 307 fEnhancedForLoop.setBody(getBody(cuRewrite, group, positionGroups)); 308 } 309 final SingleVariableDeclaration declaration= ast.newSingleVariableDeclaration(); 310 final SimpleName simple= ast.newSimpleName(name); 311 pg.addPosition(astRewrite.track(simple), true); 312 declaration.setName(simple); 313 final ITypeBinding iterable= getIterableType(fIterator.getType()); 314 declaration.setType(importType(iterable, getForStatement(), importRewrite, getRoot())); 315 if (fMakeFinal) { 316 ModifierRewrite.create(astRewrite, declaration).setModifiers(Modifier.FINAL, 0, group); 317 } 318 remover.registerAddedImport(iterable.getQualifiedName()); 319 fEnhancedForLoop.setParameter(declaration); 320 fEnhancedForLoop.setExpression(getExpression(astRewrite)); 321 322 remover.registerRemovedNode(getForStatement().getExpression()); 323 for (Iterator iterator= getForStatement().initializers().iterator(); iterator.hasNext();) { 324 ASTNode node= (ASTNode)iterator.next(); 325 remover.registerRemovedNode(node); 326 } 327 for (Iterator iterator= getForStatement().updaters().iterator(); iterator.hasNext();) { 328 ASTNode node= (ASTNode)iterator.next(); 329 remover.registerRemovedNode(node); 330 } 331 332 return fEnhancedForLoop; 333 } 334 335 341 public final IStatus satisfiesPreconditions() { 342 IStatus resultStatus= StatusInfo.OK_STATUS; 343 if (JavaModelUtil.is50OrHigher(getJavaProject())) { 344 resultStatus= checkExpressionCondition(); 345 if (resultStatus.getSeverity() == IStatus.ERROR) 346 return resultStatus; 347 348 List updateExpressions= (List )getForStatement().getStructuralProperty(ForStatement.UPDATERS_PROPERTY); 349 if (updateExpressions.size() == 1) { 350 resultStatus= new StatusInfo(IStatus.WARNING, Messages.format(FixMessages.ConvertIterableLoopOperation_RemoveUpdateExpression_Warning, ((Expression)updateExpressions.get(0)).toString())); 351 } else if (updateExpressions.size() > 1) { 352 resultStatus= new StatusInfo(IStatus.WARNING, FixMessages.ConvertIterableLoopOperation_RemoveUpdateExpressions_Warning); 353 } 354 355 for (final Iterator outer= getForStatement().initializers().iterator(); outer.hasNext();) { 356 final Expression initializer= (Expression)outer.next(); 357 if (initializer instanceof VariableDeclarationExpression) { 358 final VariableDeclarationExpression declaration= (VariableDeclarationExpression)initializer; 359 List fragments= declaration.fragments(); 360 if (fragments.size() != 1) { 361 return new StatusInfo(IStatus.ERROR, ""); } else { 363 final VariableDeclarationFragment fragment= (VariableDeclarationFragment)fragments.get(0); 364 fragment.accept(new ASTVisitor() { 365 366 public final boolean visit(final MethodInvocation node) { 367 final IMethodBinding binding= node.resolveMethodBinding(); 368 if (binding != null) { 369 final ITypeBinding type= binding.getReturnType(); 370 if (type != null) { 371 final String qualified= type.getQualifiedName(); 372 if (qualified.startsWith("java.util.Enumeration<") || qualified.startsWith("java.util.Iterator<")) { final Expression qualifier= node.getExpression(); 374 if (qualifier != null) { 375 final ITypeBinding resolved= qualifier.resolveTypeBinding(); 376 if (resolved != null) { 377 final ITypeBinding iterable= getSuperType(resolved, "java.lang.Iterable"); if (iterable != null) { 379 fExpression= qualifier; 380 if (qualifier instanceof Name) { 381 final Name name= (Name)qualifier; 382 fIterable= name.resolveBinding(); 383 } else if (qualifier instanceof MethodInvocation) { 384 final MethodInvocation invocation= (MethodInvocation)qualifier; 385 fIterable= invocation.resolveMethodBinding(); 386 } else if (qualifier instanceof FieldAccess) { 387 final FieldAccess access= (FieldAccess)qualifier; 388 fIterable= access.resolveFieldBinding(); 389 } else if (qualifier instanceof ThisExpression) 390 fIterable= resolved; 391 } 392 } 393 } else { 394 final ITypeBinding declaring= binding.getDeclaringClass(); 395 if (declaring != null) { 396 final ITypeBinding superBinding= getSuperType(declaring, "java.lang.Iterable"); if (superBinding != null) { 398 fIterable= superBinding; 399 fThis= true; 400 } 401 } 402 } 403 } 404 } 405 } 406 return true; 407 } 408 409 public final boolean visit(final VariableDeclarationFragment node) { 410 final IVariableBinding binding= node.resolveBinding(); 411 if (binding != null) { 412 final ITypeBinding type= binding.getType(); 413 if (type != null) { 414 ITypeBinding iterator= getSuperType(type, "java.util.Iterator"); if (iterator != null) 416 fIterator= binding; 417 else { 418 iterator= getSuperType(type, "java.util.Enumeration"); if (iterator != null) 420 fIterator= binding; 421 } 422 } 423 } 424 return true; 425 } 426 }); 427 } 428 } 429 } 430 final Statement statement= getForStatement().getBody(); 431 final boolean[] otherInvocationThenNext= new boolean[] {false}; 432 final int[] nextInvocationCount= new int[] {0}; 433 if (statement != null && fIterator != null) { 434 final ITypeBinding iterable= getIterableType(fIterator.getType()); 435 statement.accept(new ASTVisitor() { 436 437 public final boolean visit(final Assignment node) { 438 return visit(node.getLeftHandSide(), node.getRightHandSide()); 439 } 440 441 private boolean visit(final Expression node) { 442 if (node != null) { 443 final ITypeBinding binding= node.resolveTypeBinding(); 444 if (binding != null && iterable.equals(binding)) { 445 if (node instanceof Name) { 446 final Name name= (Name)node; 447 final IBinding result= name.resolveBinding(); 448 if (result != null) { 449 fOccurrences.add(node); 450 fElement= result; 451 return false; 452 } 453 } else if (node instanceof FieldAccess) { 454 final FieldAccess access= (FieldAccess)node; 455 final IBinding result= access.resolveFieldBinding(); 456 if (result != null) { 457 fOccurrences.add(node); 458 fElement= result; 459 return false; 460 } 461 } 462 } 463 } 464 return true; 465 } 466 467 private boolean visit(final Expression left, final Expression right) { 468 if (right instanceof MethodInvocation) { 469 final MethodInvocation invocation= (MethodInvocation)right; 470 final IMethodBinding binding= invocation.resolveMethodBinding(); 471 if (binding != null && (binding.getName().equals("next") || binding.getName().equals("nextElement"))) { final Expression expression= invocation.getExpression(); 473 if (expression instanceof Name) { 474 final Name qualifier= (Name)expression; 475 final IBinding result= qualifier.resolveBinding(); 476 if (result != null && result.equals(fIterator)) { 477 nextInvocationCount[0]++; 478 return visit(left); 479 } 480 } else if (expression instanceof FieldAccess) { 481 final FieldAccess qualifier= (FieldAccess)expression; 482 final IBinding result= qualifier.resolveFieldBinding(); 483 if (result != null && result.equals(fIterator)) { 484 nextInvocationCount[0]++; 485 return visit(left); 486 } 487 } 488 } else { 489 return visit(invocation); 490 } 491 } else if (right instanceof NullLiteral) 492 return visit(left); 493 return true; 494 } 495 496 499 public boolean visit(MethodInvocation invocation) { 500 final IMethodBinding binding= invocation.resolveMethodBinding(); 501 if (binding != null) { 502 final Expression expression= invocation.getExpression(); 503 if (expression instanceof Name) { 504 final Name qualifier= (Name)expression; 505 final IBinding result= qualifier.resolveBinding(); 506 if (result != null && result.equals(fIterator)) { 507 if (!binding.getName().equals("next") && !binding.getName().equals("nextElement")) { otherInvocationThenNext[0]= true; 509 } else { 510 nextInvocationCount[0]++; 511 } 512 } 513 } else if (expression instanceof FieldAccess) { 514 final FieldAccess qualifier= (FieldAccess)expression; 515 final IBinding result= qualifier.resolveFieldBinding(); 516 if (result != null && result.equals(fIterator)) { 517 if (!binding.getName().equals("next") && !binding.getName().equals("nextElement")) { otherInvocationThenNext[0]= true; 519 } else { 520 nextInvocationCount[0]++; 521 } 522 } 523 } 524 } 525 return false; 526 } 527 528 public final boolean visit(final VariableDeclarationFragment node) { 529 return visit(node.getName(), node.getInitializer()); 530 } 531 }); 532 if (otherInvocationThenNext[0]) 533 return new StatusInfo(IStatus.ERROR, ""); 535 if (nextInvocationCount[0] > 1) 536 return new StatusInfo(IStatus.ERROR, ""); } 538 final ASTNode root= getForStatement().getRoot(); 539 if (root != null) { 540 root.accept(new ASTVisitor() { 541 542 public final boolean visit(final ForStatement node) { 543 return false; 544 } 545 546 public final boolean visit(final SimpleName node) { 547 final IBinding binding= node.resolveBinding(); 548 if (binding != null && binding.equals(fElement)) 549 fAssigned= true; 550 return false; 551 } 552 }); 553 } 554 } 555 if ((fExpression != null || fThis) && fIterable != null && fIterator != null && !fAssigned) { 556 return resultStatus; 557 } else { 558 return new StatusInfo(IStatus.ERROR, ""); } 560 } 561 562 private IStatus checkExpressionCondition() { 563 String warningLable= FixMessages.ConvertIterableLoopOperation_semanticChangeWarning; 564 565 Expression expression= getForStatement().getExpression(); 566 if (!(expression instanceof MethodInvocation)) 567 return new StatusInfo(IStatus.WARNING, warningLable); 568 569 MethodInvocation invoc= (MethodInvocation)expression; 570 IMethodBinding methodBinding= invoc.resolveMethodBinding(); 571 if (methodBinding == null) 572 return new StatusInfo(IStatus.ERROR, ""); 574 ITypeBinding declaringClass= methodBinding.getDeclaringClass(); 575 if (declaringClass == null) 576 return new StatusInfo(IStatus.ERROR, ""); 578 String qualifiedName= declaringClass.getQualifiedName(); 579 String methodName= invoc.getName().getIdentifier(); 580 if (qualifiedName.startsWith("java.util.Enumeration")) { if (!methodName.equals("hasMoreElements")) return new StatusInfo(IStatus.WARNING, warningLable); 583 } else if (qualifiedName.startsWith("java.util.Iterator")) { if (!methodName.equals("hasNext")) return new StatusInfo(IStatus.WARNING, warningLable); 586 } else { 587 return new StatusInfo(IStatus.WARNING, warningLable); 588 } 589 590 return StatusInfo.OK_STATUS; 591 } 592 593 } 594 | Popular Tags |