1 11 package org.eclipse.jdt.internal.corext.refactoring.code; 12 13 import java.util.ArrayList ; 14 import java.util.Arrays ; 15 import java.util.HashMap ; 16 import java.util.HashSet ; 17 import java.util.Iterator ; 18 import java.util.List ; 19 import java.util.Map ; 20 import java.util.Set ; 21 import java.util.StringTokenizer ; 22 23 import org.eclipse.text.edits.MultiTextEdit; 24 import org.eclipse.text.edits.TextEdit; 25 import org.eclipse.text.edits.TextEditGroup; 26 27 import org.eclipse.core.runtime.Assert; 28 import org.eclipse.core.runtime.CoreException; 29 import org.eclipse.core.runtime.IPath; 30 import org.eclipse.core.runtime.IProgressMonitor; 31 import org.eclipse.core.runtime.IStatus; 32 import org.eclipse.core.runtime.OperationCanceledException; 33 import org.eclipse.core.runtime.Status; 34 import org.eclipse.core.runtime.SubProgressMonitor; 35 36 import org.eclipse.core.filebuffers.FileBuffers; 37 import org.eclipse.core.filebuffers.ITextFileBufferManager; 38 import org.eclipse.core.filebuffers.LocationKind; 39 40 import org.eclipse.core.resources.IFile; 41 42 import org.eclipse.jface.text.BadLocationException; 43 import org.eclipse.jface.text.IDocument; 44 45 import org.eclipse.ltk.core.refactoring.Change; 46 import org.eclipse.ltk.core.refactoring.RefactoringChangeDescriptor; 47 import org.eclipse.ltk.core.refactoring.RefactoringDescriptor; 48 import org.eclipse.ltk.core.refactoring.RefactoringStatus; 49 import org.eclipse.ltk.core.refactoring.TextFileChange; 50 import org.eclipse.ltk.core.refactoring.participants.RefactoringArguments; 51 import org.eclipse.ltk.core.refactoring.participants.ResourceChangeChecker; 52 53 import org.eclipse.jdt.core.ICompilationUnit; 54 import org.eclipse.jdt.core.IJavaElement; 55 import org.eclipse.jdt.core.IJavaProject; 56 import org.eclipse.jdt.core.JavaModelException; 57 import org.eclipse.jdt.core.dom.AST; 58 import org.eclipse.jdt.core.dom.ASTNode; 59 import org.eclipse.jdt.core.dom.ASTVisitor; 60 import org.eclipse.jdt.core.dom.AbstractTypeDeclaration; 61 import org.eclipse.jdt.core.dom.AnnotationTypeDeclaration; 62 import org.eclipse.jdt.core.dom.AnonymousClassDeclaration; 63 import org.eclipse.jdt.core.dom.Assignment; 64 import org.eclipse.jdt.core.dom.Block; 65 import org.eclipse.jdt.core.dom.BodyDeclaration; 66 import org.eclipse.jdt.core.dom.ChildListPropertyDescriptor; 67 import org.eclipse.jdt.core.dom.CompilationUnit; 68 import org.eclipse.jdt.core.dom.EnumDeclaration; 69 import org.eclipse.jdt.core.dom.Expression; 70 import org.eclipse.jdt.core.dom.ExpressionStatement; 71 import org.eclipse.jdt.core.dom.FieldAccess; 72 import org.eclipse.jdt.core.dom.IMethodBinding; 73 import org.eclipse.jdt.core.dom.ITypeBinding; 74 import org.eclipse.jdt.core.dom.IVariableBinding; 75 import org.eclipse.jdt.core.dom.Javadoc; 76 import org.eclipse.jdt.core.dom.MethodDeclaration; 77 import org.eclipse.jdt.core.dom.MethodInvocation; 78 import org.eclipse.jdt.core.dom.Modifier; 79 import org.eclipse.jdt.core.dom.QualifiedName; 80 import org.eclipse.jdt.core.dom.ReturnStatement; 81 import org.eclipse.jdt.core.dom.SimpleName; 82 import org.eclipse.jdt.core.dom.SingleVariableDeclaration; 83 import org.eclipse.jdt.core.dom.Type; 84 import org.eclipse.jdt.core.dom.TypeDeclaration; 85 import org.eclipse.jdt.core.dom.TypeParameter; 86 import org.eclipse.jdt.core.dom.VariableDeclaration; 87 import org.eclipse.jdt.core.dom.VariableDeclarationFragment; 88 import org.eclipse.jdt.core.dom.VariableDeclarationStatement; 89 import org.eclipse.jdt.core.dom.rewrite.ASTRewrite; 90 import org.eclipse.jdt.core.dom.rewrite.ImportRewrite; 91 import org.eclipse.jdt.core.dom.rewrite.ListRewrite; 92 import org.eclipse.jdt.core.refactoring.IJavaRefactorings; 93 import org.eclipse.jdt.core.refactoring.descriptors.JavaRefactoringDescriptor; 94 95 import org.eclipse.jdt.internal.corext.codemanipulation.StubUtility; 96 import org.eclipse.jdt.internal.corext.dom.ASTFlattener; 97 import org.eclipse.jdt.internal.corext.dom.ASTNodeFactory; 98 import org.eclipse.jdt.internal.corext.dom.ASTNodes; 99 import org.eclipse.jdt.internal.corext.dom.Bindings; 100 import org.eclipse.jdt.internal.corext.dom.BodyDeclarationRewrite; 101 import org.eclipse.jdt.internal.corext.dom.LinkedNodeFinder; 102 import org.eclipse.jdt.internal.corext.dom.Selection; 103 import org.eclipse.jdt.internal.corext.dom.StatementRewrite; 104 import org.eclipse.jdt.internal.corext.refactoring.Checks; 105 import org.eclipse.jdt.internal.corext.refactoring.JDTRefactoringDescriptor; 106 import org.eclipse.jdt.internal.corext.refactoring.JDTRefactoringDescriptorComment; 107 import org.eclipse.jdt.internal.corext.refactoring.JavaRefactoringArguments; 108 import org.eclipse.jdt.internal.corext.refactoring.ParameterInfo; 109 import org.eclipse.jdt.internal.corext.refactoring.RefactoringCoreMessages; 110 import org.eclipse.jdt.internal.corext.refactoring.changes.CompilationUnitChange; 111 import org.eclipse.jdt.internal.corext.refactoring.util.RefactoringASTParser; 112 import org.eclipse.jdt.internal.corext.refactoring.util.ResourceUtil; 113 import org.eclipse.jdt.internal.corext.refactoring.util.SelectionAwareSourceRangeComputer; 114 import org.eclipse.jdt.internal.corext.util.JdtFlags; 115 import org.eclipse.jdt.internal.corext.util.Messages; 116 117 import org.eclipse.jdt.ui.CodeGeneration; 118 import org.eclipse.jdt.ui.JavaElementLabels; 119 120 import org.eclipse.jdt.internal.ui.JavaPlugin; 121 import org.eclipse.jdt.internal.ui.viewsupport.BindingLabelProvider; 122 123 126 public class ExtractMethodRefactoring extends ScriptableRefactoring { 127 128 private static final String ATTRIBUTE_VISIBILITY= "visibility"; private static final String ATTRIBUTE_DESTINATION= "destination"; private static final String ATTRIBUTE_COMMENTS= "comments"; private static final String ATTRIBUTE_REPLACE= "replace"; private static final String ATTRIBUTE_EXCEPTIONS= "exceptions"; 134 private ICompilationUnit fCUnit; 135 private CompilationUnit fRoot; 136 private ImportRewrite fImportRewriter; 137 private int fSelectionStart; 138 private int fSelectionLength; 139 private AST fAST; 140 private ASTRewrite fRewriter; 141 private IDocument fDocument; 142 private ExtractMethodAnalyzer fAnalyzer; 143 private int fVisibility; 144 private String fMethodName; 145 private boolean fThrowRuntimeExceptions; 146 private List fParameterInfos; 147 private Set fUsedNames; 148 private boolean fGenerateJavadoc; 149 private boolean fReplaceDuplicates; 150 private SnippetFinder.Match[] fDuplicates; 151 private int fDestinationIndex= 0; 152 private ASTNode fDestination; 154 private ASTNode[] fDestinations; 156 157 private static final String EMPTY= ""; 159 private static class UsedNamesCollector extends ASTVisitor { 160 private Set result= new HashSet (); 161 private Set fIgnore= new HashSet (); 162 public static Set perform(ASTNode[] nodes) { 163 UsedNamesCollector collector= new UsedNamesCollector(); 164 for (int i= 0; i < nodes.length; i++) { 165 nodes[i].accept(collector); 166 } 167 return collector.result; 168 } 169 public boolean visit(FieldAccess node) { 170 Expression exp= node.getExpression(); 171 if (exp != null) 172 fIgnore.add(node.getName()); 173 return true; 174 } 175 public void endVisit(FieldAccess node) { 176 fIgnore.remove(node.getName()); 177 } 178 public boolean visit(MethodInvocation node) { 179 Expression exp= node.getExpression(); 180 if (exp != null) 181 fIgnore.add(node.getName()); 182 return true; 183 } 184 public void endVisit(MethodInvocation node) { 185 fIgnore.remove(node.getName()); 186 } 187 public boolean visit(QualifiedName node) { 188 fIgnore.add(node.getName()); 189 return true; 190 } 191 public void endVisit(QualifiedName node) { 192 fIgnore.remove(node.getName()); 193 } 194 public boolean visit(SimpleName node) { 195 if (!fIgnore.contains(node)) 196 result.add(node.getIdentifier()); 197 return true; 198 } 199 public boolean visit(TypeDeclaration node) { 200 return visitType(node); 201 } 202 public boolean visit(AnnotationTypeDeclaration node) { 203 return visitType(node); 204 } 205 public boolean visit(EnumDeclaration node) { 206 return visitType(node); 207 } 208 private boolean visitType(AbstractTypeDeclaration node) { 209 result.add(node.getName().getIdentifier()); 210 return false; 213 } 214 } 215 216 222 public ExtractMethodRefactoring(ICompilationUnit unit, int selectionStart, int selectionLength) throws CoreException { 223 fCUnit= unit; 224 fMethodName= "extracted"; fSelectionStart= selectionStart; 226 fSelectionLength= selectionLength; 227 fVisibility= -1; 228 if (unit != null) 229 initialize(unit); 230 } 231 232 private void initialize(ICompilationUnit cu) throws CoreException { 233 fImportRewriter= StubUtility.createImportRewrite(cu, true); 234 } 235 236 public String getName() { 237 return RefactoringCoreMessages.ExtractMethodRefactoring_name; 238 } 239 240 247 public RefactoringStatus checkInitialConditions(IProgressMonitor pm) throws CoreException { 248 RefactoringStatus result= new RefactoringStatus(); 249 pm.beginTask("", 100); 251 if (fSelectionStart < 0 || fSelectionLength == 0) 252 return mergeTextSelectionStatus(result); 253 254 IFile[] changedFiles= ResourceUtil.getFiles(new ICompilationUnit[]{fCUnit}); 255 result.merge(Checks.validateModifiesFiles(changedFiles, getValidationContext())); 256 if (result.hasFatalError()) 257 return result; 258 result.merge(ResourceChangeChecker.checkFilesToBeChanged(changedFiles, new SubProgressMonitor(pm, 1))); 259 260 fRoot= RefactoringASTParser.parseWithASTProvider(fCUnit, true, new SubProgressMonitor(pm, 99)); 261 fAST= fRoot.getAST(); 262 fRoot.accept(createVisitor()); 263 264 result.merge(fAnalyzer.checkInitialConditions(fImportRewriter)); 265 if (result.hasFatalError()) 266 return result; 267 if (fVisibility == -1) { 268 setVisibility(Modifier.PRIVATE); 269 } 270 initializeParameterInfos(); 271 initializeUsedNames(); 272 initializeDuplicates(); 273 initializeDestinations(); 274 return result; 275 } 276 277 private ASTVisitor createVisitor() throws JavaModelException { 278 fAnalyzer= new ExtractMethodAnalyzer(fCUnit, Selection.createFromStartLength(fSelectionStart, fSelectionLength)); 279 return fAnalyzer; 280 } 281 282 287 public void setMethodName(String name) { 288 fMethodName= name; 289 } 290 291 295 public String getMethodName() { 296 return fMethodName; 297 } 298 299 305 public void setVisibility(int visibility) { 306 fVisibility= visibility; 307 } 308 309 314 public int getVisibility() { 315 return fVisibility; 316 } 317 318 322 public List getParameterInfos() { 323 return fParameterInfos; 324 } 325 326 332 public void setThrowRuntimeExceptions(boolean throwRuntimeExceptions) { 333 fThrowRuntimeExceptions= throwRuntimeExceptions; 334 } 335 336 341 public RefactoringStatus checkMethodName() { 342 return Checks.checkMethodName(fMethodName); 343 } 344 345 public ASTNode[] getDestinations() { 346 return fDestinations; 347 } 348 349 public void setDestination(int index) { 350 fDestination= fDestinations[index]; 351 fDestinationIndex= index; 352 } 353 354 357 public RefactoringStatus checkParameterNames() { 358 RefactoringStatus result= new RefactoringStatus(); 359 for (Iterator iter= fParameterInfos.iterator(); iter.hasNext();) { 360 ParameterInfo parameter= (ParameterInfo)iter.next(); 361 result.merge(Checks.checkIdentifier(parameter.getNewName())); 362 for (Iterator others= fParameterInfos.iterator(); others.hasNext();) { 363 ParameterInfo other= (ParameterInfo) others.next(); 364 if (parameter != other && other.getNewName().equals(parameter.getNewName())) { 365 result.addError(Messages.format( 366 RefactoringCoreMessages.ExtractMethodRefactoring_error_sameParameter, 367 other.getNewName())); 368 return result; 369 } 370 } 371 if (parameter.isRenamed() && fUsedNames.contains(parameter.getNewName())) { 372 result.addError(Messages.format( 373 RefactoringCoreMessages.ExtractMethodRefactoring_error_nameInUse, 374 parameter.getNewName())); 375 return result; 376 } 377 } 378 return result; 379 } 380 381 384 public RefactoringStatus checkVarargOrder() { 385 for (Iterator iter= fParameterInfos.iterator(); iter.hasNext();) { 386 ParameterInfo info= (ParameterInfo)iter.next(); 387 if (info.isOldVarargs() && iter.hasNext()) { 388 return RefactoringStatus.createFatalErrorStatus(Messages.format( 389 RefactoringCoreMessages.ExtractMethodRefactoring_error_vararg_ordering, 390 info.getOldName())); 391 } 392 } 393 return new RefactoringStatus(); 394 } 395 396 401 public Set getUsedNames() { 402 return fUsedNames; 403 } 404 405 408 public RefactoringStatus checkFinalConditions(IProgressMonitor pm) throws CoreException { 409 pm.beginTask(RefactoringCoreMessages.ExtractMethodRefactoring_checking_new_name, 2); 410 pm.subTask(EMPTY); 411 412 RefactoringStatus result= checkMethodName(); 413 result.merge(checkParameterNames()); 414 result.merge(checkVarargOrder()); 415 pm.worked(1); 416 if (pm.isCanceled()) 417 throw new OperationCanceledException(); 418 419 BodyDeclaration node= fAnalyzer.getEnclosingBodyDeclaration(); 420 if (node != null) { 421 fAnalyzer.checkInput(result, fMethodName, fAST); 422 pm.worked(1); 423 } 424 pm.done(); 425 return result; 426 } 427 428 431 public Change createChange(IProgressMonitor pm) throws CoreException { 432 if (fMethodName == null) 433 return null; 434 pm.beginTask("", 2); fAnalyzer.aboutToCreateChange(); 436 BodyDeclaration declaration= fAnalyzer.getEnclosingBodyDeclaration(); 437 fRewriter= ASTRewrite.create(declaration.getAST()); 438 final Map arguments= new HashMap (); 439 String project= null; 440 IJavaProject javaProject= fCUnit.getJavaProject(); 441 if (javaProject != null) 442 project= javaProject.getElementName(); 443 ITypeBinding type= null; 444 if (fDestination instanceof AbstractTypeDeclaration) { 445 final AbstractTypeDeclaration decl= (AbstractTypeDeclaration) fDestination; 446 type= decl.resolveBinding(); 447 } else if (fDestination instanceof AnonymousClassDeclaration) { 448 final AnonymousClassDeclaration decl= (AnonymousClassDeclaration) fDestination; 449 type= decl.resolveBinding(); 450 } 451 IMethodBinding method= null; 452 final BodyDeclaration enclosing= fAnalyzer.getEnclosingBodyDeclaration(); 453 if (enclosing instanceof MethodDeclaration) { 454 final MethodDeclaration node= (MethodDeclaration) enclosing; 455 method= node.resolveBinding(); 456 } 457 final int flags= RefactoringDescriptor.STRUCTURAL_CHANGE | JavaRefactoringDescriptor.JAR_REFACTORING | JavaRefactoringDescriptor.JAR_SOURCE_ATTACHMENT; 458 final String description= Messages.format(RefactoringCoreMessages.ExtractMethodRefactoring_descriptor_description_short, fMethodName); 459 final String label= method != null ? BindingLabelProvider.getBindingLabel(method, JavaElementLabels.ALL_FULLY_QUALIFIED) : '{' + JavaElementLabels.ELLIPSIS_STRING + '}'; 460 final String header= Messages.format(RefactoringCoreMessages.ExtractMethodRefactoring_descriptor_description, new String [] { getSignature(), label, BindingLabelProvider.getBindingLabel(type, JavaElementLabels.ALL_FULLY_QUALIFIED)}); 461 final JDTRefactoringDescriptorComment comment= new JDTRefactoringDescriptorComment(project, this, header); 462 comment.addSetting(Messages.format(RefactoringCoreMessages.ExtractMethodRefactoring_name_pattern, fMethodName)); 463 comment.addSetting(Messages.format(RefactoringCoreMessages.ExtractMethodRefactoring_destination_pattern, BindingLabelProvider.getBindingLabel(type, JavaElementLabels.ALL_FULLY_QUALIFIED))); 464 String visibility= JdtFlags.getVisibilityString(fVisibility); 465 if ("".equals(visibility)) visibility= RefactoringCoreMessages.ExtractMethodRefactoring_default_visibility; 467 comment.addSetting(Messages.format(RefactoringCoreMessages.ExtractMethodRefactoring_visibility_pattern, visibility)); 468 if (fThrowRuntimeExceptions) 469 comment.addSetting(RefactoringCoreMessages.ExtractMethodRefactoring_declare_thrown_exceptions); 470 if (fReplaceDuplicates) 471 comment.addSetting(RefactoringCoreMessages.ExtractMethodRefactoring_replace_occurrences); 472 if (fGenerateJavadoc) 473 comment.addSetting(RefactoringCoreMessages.ExtractMethodRefactoring_generate_comment); 474 final JDTRefactoringDescriptor descriptor= new JDTRefactoringDescriptor(IJavaRefactorings.EXTRACT_METHOD, project, description, comment.asString(), arguments, flags); 475 arguments.put(JDTRefactoringDescriptor.ATTRIBUTE_INPUT, descriptor.elementToHandle(fCUnit)); 476 arguments.put(JDTRefactoringDescriptor.ATTRIBUTE_NAME, fMethodName); 477 arguments.put(JDTRefactoringDescriptor.ATTRIBUTE_SELECTION, new Integer (fSelectionStart).toString() + " " + new Integer (fSelectionLength).toString()); arguments.put(ATTRIBUTE_VISIBILITY, new Integer (fVisibility).toString()); 479 arguments.put(ATTRIBUTE_DESTINATION, new Integer (fDestinationIndex).toString()); 480 arguments.put(ATTRIBUTE_EXCEPTIONS, Boolean.valueOf(fThrowRuntimeExceptions).toString()); 481 arguments.put(ATTRIBUTE_COMMENTS, Boolean.valueOf(fGenerateJavadoc).toString()); 482 arguments.put(ATTRIBUTE_REPLACE, Boolean.valueOf(fReplaceDuplicates).toString()); 483 final CompilationUnitChange result= new CompilationUnitChange(RefactoringCoreMessages.ExtractMethodRefactoring_change_name, fCUnit); 484 result.setSaveMode(TextFileChange.KEEP_SAVE_STATE); 485 result.setDescriptor(new RefactoringChangeDescriptor(descriptor)); 486 487 MultiTextEdit root= new MultiTextEdit(); 488 result.setEdit(root); 489 IPath path= ((IFile)fCUnit.getPrimary().getResource()).getFullPath(); 491 ITextFileBufferManager bufferManager= FileBuffers.getTextFileBufferManager(); 492 try { 493 bufferManager.connect(path, LocationKind.IFILE, new SubProgressMonitor(pm, 1)); 494 fDocument= bufferManager.getTextFileBuffer(path, LocationKind.IFILE).getDocument(); 495 496 ASTNode[] selectedNodes= fAnalyzer.getSelectedNodes(); 497 fRewriter.setTargetSourceRangeComputer(new SelectionAwareSourceRangeComputer(selectedNodes, 498 fDocument, fSelectionStart, fSelectionLength)); 499 500 TextEditGroup substituteDesc= new TextEditGroup(Messages.format(RefactoringCoreMessages.ExtractMethodRefactoring_substitute_with_call, fMethodName)); 501 result.addTextEditGroup(substituteDesc); 502 503 MethodDeclaration mm= createNewMethod(fMethodName, true, selectedNodes, fDocument.getLineDelimiter(0), substituteDesc); 504 505 TextEditGroup insertDesc= new TextEditGroup(Messages.format(RefactoringCoreMessages.ExtractMethodRefactoring_add_method, fMethodName)); 506 result.addTextEditGroup(insertDesc); 507 508 if (fDestination == fDestinations[0]) { 509 ChildListPropertyDescriptor desc= (ChildListPropertyDescriptor)declaration.getLocationInParent(); 510 ListRewrite container= fRewriter.getListRewrite(declaration.getParent(), desc); 511 container.insertAfter(mm, declaration, insertDesc); 512 } else { 513 BodyDeclarationRewrite container= BodyDeclarationRewrite.create(fRewriter, fDestination); 514 container.insert(mm, insertDesc); 515 } 516 517 replaceDuplicates(result); 518 519 if (fImportRewriter.hasRecordedChanges()) { 520 TextEdit edit= fImportRewriter.rewriteImports(null); 521 root.addChild(edit); 522 result.addTextEditGroup(new TextEditGroup( 523 RefactoringCoreMessages.ExtractMethodRefactoring_organize_imports, 524 new TextEdit[] {edit} 525 )); 526 } 527 root.addChild(fRewriter.rewriteAST(fDocument, fCUnit.getJavaProject().getOptions(true))); 528 } catch (BadLocationException e) { 529 throw new CoreException(new Status(IStatus.ERROR, JavaPlugin.getPluginId(), IStatus.ERROR, 530 e.getMessage(), e)); 531 } finally { 532 bufferManager.disconnect(path, LocationKind.IFILE, new SubProgressMonitor(pm, 1)); 533 pm.done(); 534 } 535 return result; 536 } 537 538 543 public String getSignature() { 544 return getSignature(fMethodName); 545 } 546 547 553 public String getSignature(String methodName) { 554 MethodDeclaration method= null; 555 try { 556 method= createNewMethod(methodName, false, null, StubUtility.getLineDelimiterUsed(fCUnit), null); 557 } catch (CoreException cannotHappen) { 558 Assert.isTrue(false); 560 } catch (BadLocationException e) { 561 Assert.isTrue(false); 563 } 564 method.setBody(fAST.newBlock()); 565 ASTFlattener flattener= new ASTFlattener() { 566 public boolean visit(Block node) { 567 return false; 568 } 569 }; 570 method.accept(flattener); 571 return flattener.getResult(); 572 } 573 574 579 public int getNumberOfDuplicates() { 580 if (fDuplicates == null) 581 return 0; 582 int result=0; 583 for (int i= 0; i < fDuplicates.length; i++) { 584 if (!fDuplicates[i].isMethodBody()) 585 result++; 586 } 587 return result; 588 } 589 590 public boolean getReplaceDuplicates() { 591 return fReplaceDuplicates; 592 } 593 594 public void setReplaceDuplicates(boolean replace) { 595 fReplaceDuplicates= replace; 596 } 597 598 public void setGenerateJavadoc(boolean generate) { 599 fGenerateJavadoc= generate; 600 } 601 602 public boolean getGenerateJavadoc() { 603 return fGenerateJavadoc; 604 } 605 606 608 private void initializeParameterInfos() { 609 IVariableBinding[] arguments= fAnalyzer.getArguments(); 610 fParameterInfos= new ArrayList (arguments.length); 611 ASTNode root= fAnalyzer.getEnclosingBodyDeclaration(); 612 ParameterInfo vararg= null; 613 for (int i= 0; i < arguments.length; i++) { 614 IVariableBinding argument= arguments[i]; 615 if (argument == null) 616 continue; 617 VariableDeclaration declaration= ASTNodes.findVariableDeclaration(argument, root); 618 boolean isVarargs= declaration instanceof SingleVariableDeclaration 619 ? ((SingleVariableDeclaration)declaration).isVarargs() 620 : false; 621 ParameterInfo info= new ParameterInfo(argument, getType(declaration, isVarargs), argument.getName(), i); 622 if (isVarargs) { 623 vararg= info; 624 } else { 625 fParameterInfos.add(info); 626 } 627 } 628 if (vararg != null) { 629 fParameterInfos.add(vararg); 630 } 631 } 632 633 private void initializeUsedNames() { 634 fUsedNames= UsedNamesCollector.perform(fAnalyzer.getSelectedNodes()); 635 for (Iterator iter= fParameterInfos.iterator(); iter.hasNext();) { 636 ParameterInfo parameter= (ParameterInfo)iter.next(); 637 fUsedNames.remove(parameter.getOldName()); 638 } 639 } 640 641 private void initializeDuplicates() { 642 ASTNode start= fAnalyzer.getEnclosingBodyDeclaration(); 643 while(!(start instanceof AbstractTypeDeclaration) && !(start instanceof AnonymousClassDeclaration)) { 644 start= start.getParent(); 645 } 646 647 fDuplicates= SnippetFinder.perform(start, fAnalyzer.getSelectedNodes()); 648 fReplaceDuplicates= fDuplicates.length > 0 && ! fAnalyzer.isLiteralNodeSelected(); 649 } 650 651 private void initializeDestinations() { 652 List result= new ArrayList (); 653 BodyDeclaration decl= fAnalyzer.getEnclosingBodyDeclaration(); 654 ASTNode current= getNextParent(decl); 655 result.add(current); 656 if (decl instanceof MethodDeclaration) { 657 ITypeBinding binding= ASTNodes.getEnclosingType(current); 658 ASTNode next= getNextParent(current); 659 while (next != null && binding != null && binding.isNested() && !Modifier.isStatic(binding.getDeclaredModifiers())) { 660 result.add(next); 661 current= next; 662 binding= ASTNodes.getEnclosingType(current); 663 next= getNextParent(next); 664 } 665 } 666 fDestinations= (ASTNode[])result.toArray(new ASTNode[result.size()]); 667 fDestination= fDestinations[fDestinationIndex]; 668 } 669 670 private ASTNode getNextParent(ASTNode node) { 671 do { 672 node= node.getParent(); 673 } while (node != null && !((node instanceof AbstractTypeDeclaration) || (node instanceof AnonymousClassDeclaration))); 674 return node; 675 } 676 677 private RefactoringStatus mergeTextSelectionStatus(RefactoringStatus status) { 678 status.addFatalError(RefactoringCoreMessages.ExtractMethodRefactoring_no_set_of_statements); 679 return status; 680 } 681 682 private String getType(VariableDeclaration declaration, boolean isVarargs) { 683 String type= ASTNodes.asString(ASTNodeFactory.newType(declaration.getAST(), declaration)); 684 if (isVarargs) 685 return type + ParameterInfo.ELLIPSIS; 686 else 687 return type; 688 } 689 690 692 private ASTNode[] createCallNodes(SnippetFinder.Match duplicate) { 693 List result= new ArrayList (2); 694 695 IVariableBinding[] locals= fAnalyzer.getCallerLocals(); 696 for (int i= 0; i < locals.length; i++) { 697 result.add(createDeclaration(locals[i], null)); 698 } 699 700 MethodInvocation invocation= fAST.newMethodInvocation(); 701 invocation.setName(fAST.newSimpleName(fMethodName)); 702 List arguments= invocation.arguments(); 703 for (int i= 0; i < fParameterInfos.size(); i++) { 704 ParameterInfo parameter= ((ParameterInfo)fParameterInfos.get(i)); 705 arguments.add(ASTNodeFactory.newName(fAST, getMappedName(duplicate, parameter))); 706 } 707 708 ASTNode call; 709 int returnKind= fAnalyzer.getReturnKind(); 710 switch (returnKind) { 711 case ExtractMethodAnalyzer.ACCESS_TO_LOCAL: 712 IVariableBinding binding= fAnalyzer.getReturnLocal(); 713 if (binding != null) { 714 VariableDeclarationStatement decl= createDeclaration(getMappedBinding(duplicate, binding), invocation); 715 call= decl; 716 } else { 717 Assignment assignment= fAST.newAssignment(); 718 assignment.setLeftHandSide(ASTNodeFactory.newName(fAST, 719 getMappedBinding(duplicate, fAnalyzer.getReturnValue()).getName())); 720 assignment.setRightHandSide(invocation); 721 call= assignment; 722 } 723 break; 724 case ExtractMethodAnalyzer.RETURN_STATEMENT_VALUE: 725 ReturnStatement rs= fAST.newReturnStatement(); 726 rs.setExpression(invocation); 727 call= rs; 728 break; 729 default: 730 call= invocation; 731 } 732 733 if (call instanceof Expression && !fAnalyzer.isExpressionSelected()) { 734 call= fAST.newExpressionStatement((Expression)call); 735 } 736 result.add(call); 737 738 if (returnKind == ExtractMethodAnalyzer.RETURN_STATEMENT_VOID && !fAnalyzer.isLastStatementSelected()) { 742 result.add(fAST.newReturnStatement()); 743 } 744 return (ASTNode[])result.toArray(new ASTNode[result.size()]); 745 } 746 747 private IVariableBinding getMappedBinding(SnippetFinder.Match duplicate, IVariableBinding org) { 748 if (duplicate == null) 749 return org; 750 return duplicate.getMappedBinding(org); 751 } 752 753 private String getMappedName(SnippetFinder.Match duplicate, ParameterInfo paramter) { 754 if (duplicate == null) 755 return paramter.getOldName(); 756 return duplicate.getMappedName(paramter.getOldBinding()).getIdentifier(); 757 } 758 759 private void replaceDuplicates(CompilationUnitChange result) { 760 int numberOf= getNumberOfDuplicates(); 761 if (numberOf == 0 || !fReplaceDuplicates) 762 return; 763 String label= null; 764 if (numberOf == 1) 765 label= Messages.format(RefactoringCoreMessages.ExtractMethodRefactoring_duplicates_single, fMethodName); 766 else 767 label= Messages.format(RefactoringCoreMessages.ExtractMethodRefactoring_duplicates_multi, fMethodName); 768 769 TextEditGroup description= new TextEditGroup(label); 770 result.addTextEditGroup(description); 771 772 for (int d= 0; d < fDuplicates.length; d++) { 773 SnippetFinder.Match duplicate= fDuplicates[d]; 774 if (!duplicate.isMethodBody()) { 775 ASTNode[] callNodes= createCallNodes(duplicate); 776 new StatementRewrite(fRewriter, duplicate.getNodes()).replace(callNodes, description); 777 } 778 } 779 } 780 781 private MethodDeclaration createNewMethod(String name, boolean code, ASTNode[] selectedNodes, String lineDelimiter, TextEditGroup substitute) throws CoreException, BadLocationException { 782 MethodDeclaration result= fAST.newMethodDeclaration(); 783 int modifiers= fVisibility; 784 if (Modifier.isStatic(fAnalyzer.getEnclosingBodyDeclaration().getModifiers()) || fAnalyzer.getForceStatic()) { 785 modifiers|= Modifier.STATIC; 786 } 787 ITypeBinding[] typeVariables= computeLocalTypeVariables(); 788 List typeParameters= result.typeParameters(); 789 for (int i= 0; i < typeVariables.length; i++) { 790 TypeParameter parameter= fAST.newTypeParameter(); 791 parameter.setName(fAST.newSimpleName(typeVariables[i].getName())); 792 typeParameters.add(parameter); 793 } 794 795 result.modifiers().addAll(ASTNodeFactory.newModifiers(fAST, modifiers)); 796 result.setReturnType2((Type)ASTNode.copySubtree(fAST, fAnalyzer.getReturnType())); 797 result.setName(fAST.newSimpleName(name)); 798 799 List parameters= result.parameters(); 800 for (int i= 0; i < fParameterInfos.size(); i++) { 801 ParameterInfo info= (ParameterInfo)fParameterInfos.get(i); 802 VariableDeclaration infoDecl= getVariableDeclaration(info); 803 SingleVariableDeclaration parameter= fAST.newSingleVariableDeclaration(); 804 parameter.modifiers().addAll(ASTNodeFactory.newModifiers(fAST, ASTNodes.getModifiers(infoDecl))); 805 parameter.setType(ASTNodeFactory.newType(fAST, infoDecl)); 806 parameter.setName(fAST.newSimpleName(info.getNewName())); 807 parameter.setVarargs(info.isNewVarargs()); 808 parameters.add(parameter); 809 } 810 811 List exceptions= result.thrownExceptions(); 812 ITypeBinding[] exceptionTypes= fAnalyzer.getExceptions(fThrowRuntimeExceptions, fAST); 813 for (int i= 0; i < exceptionTypes.length; i++) { 814 ITypeBinding exceptionType= exceptionTypes[i]; 815 exceptions.add(ASTNodeFactory.newName(fAST, fImportRewriter.addImport(exceptionType))); 816 } 817 if (code) { 818 result.setBody(createMethodBody(result, selectedNodes, substitute)); 819 if (fGenerateJavadoc) { 820 AbstractTypeDeclaration enclosingType= 821 (AbstractTypeDeclaration)ASTNodes.getParent(fAnalyzer.getEnclosingBodyDeclaration(), AbstractTypeDeclaration.class); 822 String string= CodeGeneration.getMethodComment(fCUnit, enclosingType.getName().getIdentifier(), result, null, lineDelimiter); 823 if (string != null) { 824 Javadoc javadoc= (Javadoc)fRewriter.createStringPlaceholder(string, ASTNode.JAVADOC); 825 result.setJavadoc(javadoc); 826 } 827 } 828 } 829 830 return result; 831 } 832 833 private ITypeBinding[] computeLocalTypeVariables() { 834 List result= new ArrayList (Arrays.asList(fAnalyzer.getTypeVariables())); 835 for (int i= 0; i < fParameterInfos.size(); i++) { 836 ParameterInfo info= (ParameterInfo)fParameterInfos.get(i); 837 processVariable(result, info.getOldBinding()); 838 } 839 IVariableBinding[] methodLocals= fAnalyzer.getMethodLocals(); 840 for (int i= 0; i < methodLocals.length; i++) { 841 processVariable(result, methodLocals[i]); 842 } 843 return (ITypeBinding[])result.toArray(new ITypeBinding[result.size()]); 844 } 845 846 private void processVariable(List result, IVariableBinding variable) { 847 if (variable == null) 848 return; 849 ITypeBinding binding= variable.getType(); 850 if (binding != null && binding.isParameterizedType()) { 851 ITypeBinding[] typeArgs= binding.getTypeArguments(); 852 for (int args= 0; args < typeArgs.length; args++) { 853 ITypeBinding arg= typeArgs[args]; 854 if (arg.isTypeVariable() && !result.contains(arg)) { 855 ASTNode decl= fRoot.findDeclaringNode(arg); 856 if (decl != null && decl.getParent() instanceof MethodDeclaration) { 857 result.add(arg); 858 } 859 } 860 } 861 } 862 } 863 864 private Block createMethodBody(MethodDeclaration method, ASTNode[] selectedNodes, TextEditGroup substitute) throws BadLocationException, CoreException { 865 Block result= fAST.newBlock(); 866 ListRewrite statements= fRewriter.getListRewrite(result, Block.STATEMENTS_PROPERTY); 867 868 IVariableBinding[] methodLocals= fAnalyzer.getMethodLocals(); 871 for (int i= 0; i < methodLocals.length; i++) { 872 if (methodLocals[i] != null) { 873 result.statements().add(createDeclaration(methodLocals[i], null)); 874 } 875 } 876 877 for (Iterator iter= fParameterInfos.iterator(); iter.hasNext();) { 878 ParameterInfo parameter= (ParameterInfo)iter.next(); 879 if (parameter.isRenamed()) { 880 for (int n= 0; n < selectedNodes.length; n++) { 881 SimpleName[] oldNames= LinkedNodeFinder.findByBinding(selectedNodes[n], parameter.getOldBinding()); 882 for (int i= 0; i < oldNames.length; i++) { 883 fRewriter.replace(oldNames[i], fAST.newSimpleName(parameter.getNewName()), null); 884 } 885 } 886 } 887 } 888 889 boolean extractsExpression= fAnalyzer.isExpressionSelected(); 890 ASTNode[] callNodes= createCallNodes(null); 891 ASTNode replacementNode; 892 if (callNodes.length == 1) { 893 replacementNode= callNodes[0]; 894 } else { 895 replacementNode= fRewriter.createGroupNode(callNodes); 896 } 897 if (extractsExpression) { 898 ITypeBinding binding= fAnalyzer.getExpressionBinding(); 900 if (binding != null && (!binding.isPrimitive() || !"void".equals(binding.getName()))) { ReturnStatement rs= fAST.newReturnStatement(); 902 rs.setExpression((Expression)fRewriter.createMoveTarget(selectedNodes[0])); 903 statements.insertLast(rs, null); 904 } else { 905 ExpressionStatement st= fAST.newExpressionStatement((Expression)fRewriter.createMoveTarget(selectedNodes[0])); 906 statements.insertLast(st, null); 907 } 908 fRewriter.replace(selectedNodes[0], replacementNode, substitute); 909 } else { 910 if (selectedNodes.length == 1) { 911 statements.insertLast(fRewriter.createMoveTarget(selectedNodes[0]), substitute); 912 fRewriter.replace(selectedNodes[0], replacementNode, substitute); 913 } else { 914 ListRewrite source= fRewriter.getListRewrite( 915 selectedNodes[0].getParent(), 916 (ChildListPropertyDescriptor)selectedNodes[0].getLocationInParent()); 917 ASTNode toMove= source.createMoveTarget( 918 selectedNodes[0], selectedNodes[selectedNodes.length - 1], 919 replacementNode, substitute); 920 statements.insertLast(toMove, substitute); 921 } 922 IVariableBinding returnValue= fAnalyzer.getReturnValue(); 923 if (returnValue != null) { 924 ReturnStatement rs= fAST.newReturnStatement(); 925 rs.setExpression(fAST.newSimpleName(getName(returnValue))); 926 statements.insertLast(rs, null); 927 } 928 } 929 return result; 930 } 931 932 private String getName(IVariableBinding binding) { 933 for (Iterator iter= fParameterInfos.iterator(); iter.hasNext();) { 934 ParameterInfo info= (ParameterInfo)iter.next(); 935 if (Bindings.equals(binding, info.getOldBinding())) { 936 return info.getNewName(); 937 } 938 } 939 return binding.getName(); 940 } 941 942 private VariableDeclaration getVariableDeclaration(ParameterInfo parameter) { 943 return ASTNodes.findVariableDeclaration(parameter.getOldBinding(), fAnalyzer.getEnclosingBodyDeclaration()); 944 } 945 946 private VariableDeclarationStatement createDeclaration(IVariableBinding binding, Expression intilizer) { 947 VariableDeclaration original= ASTNodes.findVariableDeclaration(binding, fAnalyzer.getEnclosingBodyDeclaration()); 948 VariableDeclarationFragment fragment= fAST.newVariableDeclarationFragment(); 949 fragment.setName((SimpleName)ASTNode.copySubtree(fAST, original.getName())); 950 fragment.setInitializer(intilizer); 951 VariableDeclarationStatement result= fAST.newVariableDeclarationStatement(fragment); 952 result.modifiers().addAll(ASTNode.copySubtrees(fAST, ASTNodes.getModifiers(original))); 953 result.setType(ASTNodeFactory.newType(fAST, original)); 954 return result; 955 } 956 957 public ICompilationUnit getCompilationUnit() { 958 return fCUnit; 959 } 960 961 public RefactoringStatus initialize(final RefactoringArguments arguments) { 962 if (arguments instanceof JavaRefactoringArguments) { 963 final JavaRefactoringArguments extended= (JavaRefactoringArguments) arguments; 964 final String selection= extended.getAttribute(JDTRefactoringDescriptor.ATTRIBUTE_SELECTION); 965 if (selection != null) { 966 int offset= -1; 967 int length= -1; 968 final StringTokenizer tokenizer= new StringTokenizer (selection); 969 if (tokenizer.hasMoreTokens()) 970 offset= Integer.valueOf(tokenizer.nextToken()).intValue(); 971 if (tokenizer.hasMoreTokens()) 972 length= Integer.valueOf(tokenizer.nextToken()).intValue(); 973 if (offset >= 0 && length >= 0) { 974 fSelectionStart= offset; 975 fSelectionLength= length; 976 } else 977 return RefactoringStatus.createFatalErrorStatus(Messages.format(RefactoringCoreMessages.InitializableRefactoring_illegal_argument, new Object [] { selection, JDTRefactoringDescriptor.ATTRIBUTE_SELECTION})); 978 } else 979 return RefactoringStatus.createFatalErrorStatus(Messages.format(RefactoringCoreMessages.InitializableRefactoring_argument_not_exist, JDTRefactoringDescriptor.ATTRIBUTE_SELECTION)); 980 final String handle= extended.getAttribute(JDTRefactoringDescriptor.ATTRIBUTE_INPUT); 981 if (handle != null) { 982 final IJavaElement element= JDTRefactoringDescriptor.handleToElement(extended.getProject(), handle, false); 983 if (element == null || !element.exists() || element.getElementType() != IJavaElement.COMPILATION_UNIT) 984 return createInputFatalStatus(element, IJavaRefactorings.EXTRACT_METHOD); 985 else { 986 fCUnit= (ICompilationUnit) element; 987 try { 988 initialize(fCUnit); 989 } catch (CoreException exception) { 990 JavaPlugin.log(exception); 991 } 992 } 993 } else 994 return RefactoringStatus.createFatalErrorStatus(Messages.format(RefactoringCoreMessages.InitializableRefactoring_argument_not_exist, JDTRefactoringDescriptor.ATTRIBUTE_INPUT)); 995 final String visibility= extended.getAttribute(ATTRIBUTE_VISIBILITY); 996 if (visibility != null && !"".equals(visibility)) { int flag= 0; 998 try { 999 flag= Integer.parseInt(visibility); 1000 } catch (NumberFormatException exception) { 1001 return RefactoringStatus.createFatalErrorStatus(Messages.format(RefactoringCoreMessages.InitializableRefactoring_argument_not_exist, ATTRIBUTE_VISIBILITY)); 1002 } 1003 fVisibility= flag; 1004 } 1005 final String name= extended.getAttribute(JDTRefactoringDescriptor.ATTRIBUTE_NAME); 1006 if (name != null && !"".equals(name)) fMethodName= name; 1008 else 1009 return RefactoringStatus.createFatalErrorStatus(Messages.format(RefactoringCoreMessages.InitializableRefactoring_argument_not_exist, JDTRefactoringDescriptor.ATTRIBUTE_NAME)); 1010 final String destination= extended.getAttribute(ATTRIBUTE_DESTINATION); 1011 if (destination != null && !"".equals(destination)) { int index= 0; 1013 try { 1014 index= Integer.parseInt(destination); 1015 } catch (NumberFormatException exception) { 1016 return RefactoringStatus.createFatalErrorStatus(Messages.format(RefactoringCoreMessages.InitializableRefactoring_argument_not_exist, ATTRIBUTE_DESTINATION)); 1017 } 1018 fDestinationIndex= index; 1019 } 1020 final String replace= extended.getAttribute(ATTRIBUTE_REPLACE); 1021 if (replace != null) { 1022 fReplaceDuplicates= Boolean.valueOf(replace).booleanValue(); 1023 } else 1024 return RefactoringStatus.createFatalErrorStatus(Messages.format(RefactoringCoreMessages.InitializableRefactoring_argument_not_exist, ATTRIBUTE_REPLACE)); 1025 final String comments= extended.getAttribute(ATTRIBUTE_COMMENTS); 1026 if (comments != null) 1027 fGenerateJavadoc= Boolean.valueOf(comments).booleanValue(); 1028 else 1029 return RefactoringStatus.createFatalErrorStatus(Messages.format(RefactoringCoreMessages.InitializableRefactoring_argument_not_exist, ATTRIBUTE_COMMENTS)); 1030 final String exceptions= extended.getAttribute(ATTRIBUTE_EXCEPTIONS); 1031 if (exceptions != null) 1032 fThrowRuntimeExceptions= Boolean.valueOf(exceptions).booleanValue(); 1033 else 1034 return RefactoringStatus.createFatalErrorStatus(Messages.format(RefactoringCoreMessages.InitializableRefactoring_argument_not_exist, ATTRIBUTE_EXCEPTIONS)); 1035 } else 1036 return RefactoringStatus.createFatalErrorStatus(RefactoringCoreMessages.InitializableRefactoring_inacceptable_arguments); 1037 return new RefactoringStatus(); 1038 } 1039} 1040 | Popular Tags |