1 11 package org.eclipse.jdt.internal.corext.fix; 12 13 import java.util.HashSet ; 14 import java.util.List ; 15 16 import org.eclipse.text.edits.TextEditGroup; 17 18 import org.eclipse.core.runtime.CoreException; 19 import org.eclipse.core.runtime.IStatus; 20 import org.eclipse.core.runtime.Status; 21 22 import org.eclipse.jdt.core.IJavaElement; 23 import org.eclipse.jdt.core.IJavaProject; 24 import org.eclipse.jdt.core.dom.AST; 25 import org.eclipse.jdt.core.dom.ASTNode; 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.IVariableBinding; 36 import org.eclipse.jdt.core.dom.InfixExpression; 37 import org.eclipse.jdt.core.dom.Modifier; 38 import org.eclipse.jdt.core.dom.Name; 39 import org.eclipse.jdt.core.dom.NumberLiteral; 40 import org.eclipse.jdt.core.dom.PostfixExpression; 41 import org.eclipse.jdt.core.dom.PrefixExpression; 42 import org.eclipse.jdt.core.dom.PrimitiveType; 43 import org.eclipse.jdt.core.dom.QualifiedName; 44 import org.eclipse.jdt.core.dom.SimpleName; 45 import org.eclipse.jdt.core.dom.SingleVariableDeclaration; 46 import org.eclipse.jdt.core.dom.Statement; 47 import org.eclipse.jdt.core.dom.Type; 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.InfixExpression.Operator; 52 import org.eclipse.jdt.core.dom.rewrite.ASTRewrite; 53 import org.eclipse.jdt.core.dom.rewrite.ImportRewrite; 54 import org.eclipse.jdt.core.dom.rewrite.ListRewrite; 55 56 import org.eclipse.jdt.internal.corext.codemanipulation.StubUtility; 57 import org.eclipse.jdt.internal.corext.dom.GenericVisitor; 58 import org.eclipse.jdt.internal.corext.dom.ModifierRewrite; 59 import org.eclipse.jdt.internal.corext.refactoring.structure.CompilationUnitRewrite; 60 import org.eclipse.jdt.internal.corext.refactoring.util.TightSourceRangeComputer; 61 import org.eclipse.jdt.internal.corext.util.JavaModelUtil; 62 63 64 public class ConvertForLoopOperation extends ConvertLoopOperation { 65 66 private static final String LENGTH_QUERY= "length"; private static final String LITERAL_0= "0"; private static final String LITERAL_1= "1"; private static final class InvalidBodyError extends Error { 70 private static final long serialVersionUID= 1L; 71 } 72 73 private IVariableBinding fIndexBinding; 74 private IVariableBinding fLengthBinding; 75 private IBinding fArrayBinding; 76 private Expression fArrayAccess; 77 private VariableDeclarationFragment fElementDeclaration; 78 private final boolean fMakeFinal; 79 80 public ConvertForLoopOperation(ForStatement forStatement) { 81 this(forStatement, new String [0], false); 82 } 83 84 public ConvertForLoopOperation(ForStatement forStatement, String [] usedNames, boolean makeFinal) { 85 super(forStatement, usedNames); 86 fMakeFinal= makeFinal; 87 } 88 89 public IStatus satisfiesPreconditions() { 90 ForStatement statement= getForStatement(); 91 CompilationUnit ast= (CompilationUnit)statement.getRoot(); 92 93 IJavaElement javaElement= ast.getJavaElement(); 94 if (javaElement == null) 95 return ERROR_STATUS; 96 97 if (!JavaModelUtil.is50OrHigher(javaElement.getJavaProject())) 98 return ERROR_STATUS; 99 100 if (!validateInitializers(statement)) 101 return ERROR_STATUS; 102 103 if (!validateExpression(statement)) 104 return ERROR_STATUS; 105 106 if (!validateUpdaters(statement)) 107 return ERROR_STATUS; 108 109 if (!validateBody(statement)) 110 return ERROR_STATUS; 111 112 return Status.OK_STATUS; 113 } 114 115 123 private boolean validateInitializers(ForStatement statement) { 124 List initializers= statement.initializers(); 125 if (initializers.size() != 1) 126 return false; 127 128 Expression expression= (Expression)initializers.get(0); 129 if (!(expression instanceof VariableDeclarationExpression)) 130 return false; 131 132 VariableDeclarationExpression declaration= (VariableDeclarationExpression)expression; 133 ITypeBinding declarationBinding= declaration.resolveTypeBinding(); 134 if (declarationBinding == null) 135 return false; 136 137 if (!declarationBinding.isPrimitive()) 138 return false; 139 140 if (!PrimitiveType.INT.toString().equals(declarationBinding.getQualifiedName())) 141 return false; 142 143 List fragments= declaration.fragments(); 144 if (fragments.size() == 1) { 145 IVariableBinding indexBinding= getIndexBindingFromFragment((VariableDeclarationFragment)fragments.get(0)); 146 if (indexBinding == null) 147 return false; 148 149 fIndexBinding= indexBinding; 150 return true; 151 } else if (fragments.size() == 2) { 152 IVariableBinding indexBinding= getIndexBindingFromFragment((VariableDeclarationFragment)fragments.get(0)); 153 if (indexBinding == null) { 154 indexBinding= getIndexBindingFromFragment((VariableDeclarationFragment)fragments.get(1)); 155 if (indexBinding == null) 156 return false; 157 158 if (!validateLengthFragment((VariableDeclarationFragment)fragments.get(0))) 159 return false; 160 } else { 161 if (!validateLengthFragment((VariableDeclarationFragment)fragments.get(1))) 162 return false; 163 } 164 165 fIndexBinding= indexBinding; 166 return true; 167 } 168 return false; 169 } 170 171 174 private boolean validateLengthFragment(VariableDeclarationFragment fragment) { 175 Expression initializer= fragment.getInitializer(); 176 if (initializer == null) 177 return false; 178 179 if (!validateLengthQuery(initializer)) 180 return false; 181 182 IVariableBinding lengthBinding= (IVariableBinding)fragment.getName().resolveBinding(); 183 if (lengthBinding == null) 184 return false; 185 fLengthBinding= lengthBinding; 186 187 return true; 188 } 189 190 196 private IVariableBinding getIndexBindingFromFragment(VariableDeclarationFragment fragment) { 197 Expression initializer= fragment.getInitializer(); 198 if (!(initializer instanceof NumberLiteral)) 199 return null; 200 201 NumberLiteral number= (NumberLiteral)initializer; 202 if (!LITERAL_0.equals(number.getToken())) 203 return null; 204 205 return (IVariableBinding)fragment.getName().resolveBinding(); 206 } 207 208 217 private boolean validateExpression(ForStatement statement) { 218 Expression expression= statement.getExpression(); 219 if (!(expression instanceof InfixExpression)) 220 return false; 221 222 InfixExpression infix= (InfixExpression)expression; 223 224 Expression left= infix.getLeftOperand(); 225 Expression right= infix.getRightOperand(); 226 if (left instanceof SimpleName && right instanceof SimpleName) { 227 IVariableBinding lengthBinding= fLengthBinding; 228 if (lengthBinding == null) 229 return false; 230 231 IBinding leftBinding= ((SimpleName)left).resolveBinding(); 232 IBinding righBinding= ((SimpleName)right).resolveBinding(); 233 234 if (fIndexBinding.equals(leftBinding)) { 235 return lengthBinding.equals(righBinding); 236 } else if (fIndexBinding.equals(righBinding)) { 237 return lengthBinding.equals(leftBinding); 238 } 239 240 return false; 241 } else if (left instanceof SimpleName) { 242 if (!fIndexBinding.equals(((SimpleName)left).resolveBinding())) 243 return false; 244 245 if (!Operator.LESS.equals(infix.getOperator())) 246 return false; 247 248 return validateLengthQuery(right); 249 } else if (right instanceof SimpleName) { 250 if (!fIndexBinding.equals(((SimpleName)right).resolveBinding())) 251 return false; 252 253 if (!Operator.GREATER.equals(infix.getOperator())) 254 return false; 255 256 return validateLengthQuery(left); 257 } 258 259 return false; 260 } 261 262 268 private boolean validateLengthQuery(Expression lengthQuery) { 269 if (lengthQuery instanceof QualifiedName) { 270 QualifiedName qualifiedName= (QualifiedName)lengthQuery; 271 SimpleName name= qualifiedName.getName(); 272 if (!LENGTH_QUERY.equals(name.getIdentifier())) 273 return false; 274 275 Name arrayAccess= qualifiedName.getQualifier(); 276 ITypeBinding accessType= arrayAccess.resolveTypeBinding(); 277 if (accessType == null) 278 return false; 279 280 if (!accessType.isArray()) 281 return false; 282 283 IBinding arrayBinding= arrayAccess.resolveBinding(); 284 if (arrayBinding == null) 285 return false; 286 287 fArrayBinding= arrayBinding; 288 fArrayAccess= arrayAccess; 289 return true; 290 } else if (lengthQuery instanceof FieldAccess) { 291 FieldAccess fieldAccess= (FieldAccess)lengthQuery; 292 SimpleName name= fieldAccess.getName(); 293 if (!LENGTH_QUERY.equals(name.getIdentifier())) 294 return false; 295 296 Expression arrayAccess= fieldAccess.getExpression(); 297 ITypeBinding accessType= arrayAccess.resolveTypeBinding(); 298 if (accessType == null) 299 return false; 300 301 if (!accessType.isArray()) 302 return false; 303 304 IBinding arrayBinding= getBinding(arrayAccess); 305 if (arrayBinding == null) 306 return false; 307 308 fArrayBinding= arrayBinding; 309 fArrayAccess= arrayAccess; 310 return true; 311 } 312 313 return false; 314 } 315 316 325 private boolean validateUpdaters(ForStatement statement) { 326 List updaters= statement.updaters(); 327 if (updaters.size() != 1) 328 return false; 329 330 Expression updater= (Expression)updaters.get(0); 331 if (updater instanceof PostfixExpression) { 332 PostfixExpression postfix= (PostfixExpression)updater; 333 334 if (!PostfixExpression.Operator.INCREMENT.equals(postfix.getOperator())) 335 return false; 336 337 IBinding binding= getBinding(postfix.getOperand()); 338 if (!fIndexBinding.equals(binding)) 339 return false; 340 341 return true; 342 } else if (updater instanceof Assignment) { 343 Assignment assignment= (Assignment)updater; 344 Expression left= assignment.getLeftHandSide(); 345 IBinding binding= getBinding(left); 346 if (!fIndexBinding.equals(binding)) 347 return false; 348 349 if (Assignment.Operator.PLUS_ASSIGN.equals(assignment.getOperator())) { 350 return isOneLiteral(assignment.getRightHandSide()); 351 } else if (Assignment.Operator.ASSIGN.equals(assignment.getOperator())) { 352 Expression right= assignment.getRightHandSide(); 353 if (!(right instanceof InfixExpression)) 354 return false; 355 356 InfixExpression infixExpression= (InfixExpression)right; 357 Expression leftOperand= infixExpression.getLeftOperand(); 358 IBinding leftBinding= getBinding(leftOperand); 359 Expression rightOperand= infixExpression.getRightOperand(); 360 IBinding rightBinding= getBinding(rightOperand); 361 362 if (fIndexBinding.equals(leftBinding)) { 363 return isOneLiteral(rightOperand); 364 } else if (fIndexBinding.equals(rightBinding)) { 365 return isOneLiteral(leftOperand); 366 } 367 } 368 } 369 return false; 370 } 371 372 private boolean isOneLiteral(Expression expression) { 373 if (!(expression instanceof NumberLiteral)) 374 return false; 375 376 NumberLiteral literal= (NumberLiteral)expression; 377 return LITERAL_1.equals(literal.getToken()); 378 } 379 380 391 private boolean validateBody(ForStatement statement) { 392 Statement body= statement.getBody(); 393 try { 394 body.accept(new GenericVisitor() { 395 398 protected boolean visitNode(ASTNode node) { 399 if (node instanceof Name) { 400 Name name= (Name)node; 401 IBinding nameBinding= name.resolveBinding(); 402 if (nameBinding == null) 403 throw new InvalidBodyError(); 404 405 if (nameBinding.equals(fIndexBinding)) { 406 if (node.getLocationInParent() != ArrayAccess.INDEX_PROPERTY) 407 throw new InvalidBodyError(); 408 409 ArrayAccess arrayAccess= (ArrayAccess)node.getParent(); 410 Expression array= arrayAccess.getArray(); 411 412 IBinding binding= getBinding(array); 413 if (binding == null) 414 throw new InvalidBodyError(); 415 416 if (!fArrayBinding.equals(binding)) 417 throw new InvalidBodyError(); 418 419 } else if (nameBinding.equals(fArrayBinding)) { 420 ASTNode current= node; 421 while (current != null && !(current instanceof Statement)) { 422 if (current.getLocationInParent() == Assignment.LEFT_HAND_SIDE_PROPERTY) 423 throw new InvalidBodyError(); 424 425 if (current instanceof PrefixExpression) 426 throw new InvalidBodyError(); 427 428 if (current instanceof PostfixExpression) 429 throw new InvalidBodyError(); 430 431 current= current.getParent(); 432 } 433 } else if (nameBinding.equals(fLengthBinding)) { 434 throw new InvalidBodyError(); 435 } 436 } 437 438 return true; 439 } 440 441 public boolean visit(ArrayAccess node) { 442 if (fElementDeclaration != null) 443 return super.visit(node); 444 445 IBinding binding= getBinding(node.getArray()); 446 if (fArrayBinding.equals(binding)) { 447 IBinding index= getBinding(node.getIndex()); 448 if (fIndexBinding.equals(index)) { 449 if (node.getLocationInParent() == VariableDeclarationFragment.INITIALIZER_PROPERTY) { 450 fElementDeclaration= (VariableDeclarationFragment)node.getParent(); 451 } 452 } 453 } 454 return super.visit(node); 455 } 456 457 }); 458 } catch (InvalidBodyError e) { 459 return false; 460 } 461 462 return true; 463 } 464 465 private static IBinding getBinding(Expression expression) { 466 if (expression instanceof FieldAccess) { 467 return ((FieldAccess)expression).resolveFieldBinding(); 468 } else if (expression instanceof Name) { 469 return ((Name)expression).resolveBinding(); 470 } 471 472 return null; 473 } 474 475 public String getIntroducedVariableName() { 476 if (fElementDeclaration != null) { 477 return fElementDeclaration.getName().getIdentifier(); 478 } else { 479 ForStatement forStatement= getForStatement(); 480 IJavaProject javaProject= ((CompilationUnit)forStatement.getRoot()).getJavaElement().getJavaProject(); 481 String [] proposals= getVariableNameProposals(fArrayAccess.resolveTypeBinding(), javaProject); 482 return proposals[0]; 483 } 484 } 485 486 489 public void rewriteAST(CompilationUnitRewrite cuRewrite, List textEditGroups, LinkedProposalModel positionGroups) throws CoreException { 490 TextEditGroup group= createTextEditGroup(FixMessages.Java50Fix_ConvertToEnhancedForLoop_description); 491 textEditGroups.add(group); 492 ASTRewrite rewrite= cuRewrite.getASTRewrite(); 493 494 TightSourceRangeComputer rangeComputer; 495 if (rewrite.getExtendedSourceRangeComputer() instanceof TightSourceRangeComputer) { 496 rangeComputer= (TightSourceRangeComputer)rewrite.getExtendedSourceRangeComputer(); 497 } else { 498 rangeComputer= new TightSourceRangeComputer(); 499 } 500 rangeComputer.addTightSourceNode(getForStatement()); 501 rewrite.setTargetSourceRangeComputer(rangeComputer); 502 503 Statement statement= convert(cuRewrite, group, positionGroups); 504 rewrite.replace(getForStatement(), statement, group); 505 } 506 507 protected Statement convert(CompilationUnitRewrite cuRewrite, TextEditGroup group, LinkedProposalModel positionGroups) throws CoreException { 508 ASTRewrite rewrite= cuRewrite.getASTRewrite(); 509 ImportRewrite importRewrite= cuRewrite.getImportRewrite(); 510 511 ForStatement forStatement= getForStatement(); 512 513 IJavaProject javaProject= ((CompilationUnit)forStatement.getRoot()).getJavaElement().getJavaProject(); 514 String [] proposals= getVariableNameProposals(fArrayAccess.resolveTypeBinding(), javaProject); 515 516 String parameterName; 517 if (fElementDeclaration != null) { 518 parameterName= fElementDeclaration.getName().getIdentifier(); 519 } else { 520 parameterName= proposals[0]; 521 } 522 523 LinkedProposalPositionGroup pg= positionGroups.getPositionGroup(parameterName, true); 524 if (fElementDeclaration != null) 525 pg.addProposal(parameterName, null, 10); 526 for (int i= 0; i < proposals.length; i++) { 527 pg.addProposal(proposals[i], null, 10); 528 } 529 530 AST ast= forStatement.getAST(); 531 EnhancedForStatement result= ast.newEnhancedForStatement(); 532 533 SingleVariableDeclaration parameterDeclaration= createParameterDeclaration(parameterName, fElementDeclaration, fArrayAccess, forStatement, importRewrite, rewrite, group, pg, fMakeFinal); 534 result.setParameter(parameterDeclaration); 535 536 result.setExpression((Expression)rewrite.createCopyTarget(fArrayAccess)); 537 538 convertBody(forStatement.getBody(), fIndexBinding, fArrayBinding, parameterName, rewrite, group, pg); 539 result.setBody(getBody(cuRewrite, group, positionGroups)); 540 541 positionGroups.setEndPosition(rewrite.track(result)); 542 543 return result; 544 } 545 546 private void convertBody(Statement body, final IBinding indexBinding, final IBinding arrayBinding, final String parameterName, final ASTRewrite rewrite, final TextEditGroup editGroup, final LinkedProposalPositionGroup pg) { 547 final AST ast= body.getAST(); 548 549 final HashSet assignedBindings= new HashSet (); 550 551 body.accept(new GenericVisitor() { 552 public boolean visit(ArrayAccess node) { 553 IBinding binding= getBinding(node.getArray()); 554 if (arrayBinding.equals(binding)) { 555 IBinding index= getBinding(node.getIndex()); 556 if (indexBinding.equals(index)) { 557 replaceAccess(node); 558 } 559 } 560 561 return super.visit(node); 562 } 563 564 public boolean visit(SimpleName node) { 565 if (assignedBindings.contains(node.resolveBinding())) { 566 replaceAccess(node); 567 } 568 return super.visit(node); 569 } 570 571 private void replaceAccess(ASTNode node) { 572 if (node.getLocationInParent() == VariableDeclarationFragment.INITIALIZER_PROPERTY) { 573 VariableDeclarationFragment fragment= (VariableDeclarationFragment)node.getParent(); 574 IBinding targetBinding= fragment.getName().resolveBinding(); 575 if (targetBinding != null) { 576 assignedBindings.add(targetBinding); 577 578 VariableDeclarationStatement statement= (VariableDeclarationStatement)fragment.getParent(); 579 580 if (statement.fragments().size() == 1) { 581 rewrite.remove(statement, editGroup); 582 } else { 583 ListRewrite listRewrite= rewrite.getListRewrite(statement, VariableDeclarationStatement.FRAGMENTS_PROPERTY); 584 listRewrite.remove(fragment, editGroup); 585 } 586 587 } else { 588 SimpleName name= ast.newSimpleName(parameterName); 589 rewrite.replace(node, name, editGroup); 590 pg.addPosition(rewrite.track(name), true); 591 } 592 } else { 593 SimpleName name= ast.newSimpleName(parameterName); 594 rewrite.replace(node, name, editGroup); 595 pg.addPosition(rewrite.track(name), true); 596 } 597 } 598 }); 599 } 600 601 private SingleVariableDeclaration createParameterDeclaration(String parameterName, VariableDeclarationFragment fragement, Expression arrayAccess, ForStatement statement, ImportRewrite importRewrite, ASTRewrite rewrite, TextEditGroup group, LinkedProposalPositionGroup pg, boolean makeFinal) { 602 CompilationUnit compilationUnit= (CompilationUnit)arrayAccess.getRoot(); 603 AST ast= compilationUnit.getAST(); 604 605 SingleVariableDeclaration result= ast.newSingleVariableDeclaration(); 606 607 SimpleName name= ast.newSimpleName(parameterName); 608 pg.addPosition(rewrite.track(name), true); 609 result.setName(name); 610 611 ITypeBinding arrayTypeBinding= arrayAccess.resolveTypeBinding(); 612 Type type= importType(arrayTypeBinding.getElementType(), statement, importRewrite, compilationUnit); 613 if (arrayTypeBinding.getDimensions() != 1) { 614 type= ast.newArrayType(type, arrayTypeBinding.getDimensions() - 1); 615 } 616 result.setType(type); 617 618 if (fragement != null) { 619 VariableDeclarationStatement declaration= (VariableDeclarationStatement)fragement.getParent(); 620 ModifierRewrite.create(rewrite, result).copyAllModifiers(declaration, group); 621 } 622 if (makeFinal) { 623 ModifierRewrite.create(rewrite, result).setModifiers(Modifier.FINAL, 0, group); 624 } 625 626 return result; 627 } 628 629 private String [] getVariableNameProposals(ITypeBinding arrayTypeBinding, IJavaProject project) { 630 String [] variableNames= getUsedVariableNames(); 631 String [] elementSuggestions= StubUtility.getLocalNameSuggestions(project, FOR_LOOP_ELEMENT_IDENTIFIER, 0, variableNames); 632 633 String type= arrayTypeBinding.getElementType().getName(); 634 String [] typeSuggestions= StubUtility.getLocalNameSuggestions(project, type, arrayTypeBinding.getDimensions() - 1, variableNames); 635 636 String [] result= new String [elementSuggestions.length + typeSuggestions.length]; 637 System.arraycopy(elementSuggestions, 0, result, 0, elementSuggestions.length); 638 System.arraycopy(typeSuggestions, 0, result, elementSuggestions.length, typeSuggestions.length); 639 return result; 640 } 641 642 } 643 | Popular Tags |