1 /******************************************************************************* 2 * Copyright (c) 2000, 2020 IBM Corporation and others. 3 * 4 * This program and the accompanying materials 5 * are made available under the terms of the Eclipse Public License 2.0 6 * which accompanies this distribution, and is available at 7 * https://www.eclipse.org/legal/epl-2.0/ 8 * 9 * SPDX-License-Identifier: EPL-2.0 10 * 11 * Contributors: 12 * IBM Corporation - initial API and implementation 13 * Dmitry Stalnov (dstalnov@fusionone.com) - contributed fixes for: 14 * o inline call that is used in a field initializer 15 * (see https://bugs.eclipse.org/bugs/show_bug.cgi?id=38137) 16 * o Allow 'this' constructor to be inlined 17 * (see https://bugs.eclipse.org/bugs/show_bug.cgi?id=38093) 18 * Microsoft Corporation - copied to jdt.core.manipulation 19 *******************************************************************************/ 20 package org.eclipse.jdt.internal.corext.refactoring.code; 21 22 import java.util.ArrayList; 23 import java.util.HashMap; 24 import java.util.List; 25 import java.util.Map; 26 27 import org.eclipse.core.runtime.Assert; 28 import org.eclipse.core.runtime.CoreException; 29 import org.eclipse.core.runtime.IProgressMonitor; 30 import org.eclipse.core.runtime.OperationCanceledException; 31 import org.eclipse.core.runtime.SubProgressMonitor; 32 33 import org.eclipse.core.resources.IFile; 34 import org.eclipse.core.resources.IResource; 35 36 import org.eclipse.text.edits.MultiTextEdit; 37 import org.eclipse.text.edits.TextEdit; 38 import org.eclipse.text.edits.TextEditGroup; 39 40 import org.eclipse.ltk.core.refactoring.Change; 41 import org.eclipse.ltk.core.refactoring.Refactoring; 42 import org.eclipse.ltk.core.refactoring.RefactoringDescriptor; 43 import org.eclipse.ltk.core.refactoring.RefactoringStatus; 44 import org.eclipse.ltk.core.refactoring.TextChange; 45 import org.eclipse.ltk.core.refactoring.participants.ResourceChangeChecker; 46 47 import org.eclipse.jdt.core.Flags; 48 import org.eclipse.jdt.core.IClassFile; 49 import org.eclipse.jdt.core.ICompilationUnit; 50 import org.eclipse.jdt.core.IJavaProject; 51 import org.eclipse.jdt.core.IMethod; 52 import org.eclipse.jdt.core.IType; 53 import org.eclipse.jdt.core.ITypeHierarchy; 54 import org.eclipse.jdt.core.ITypeRoot; 55 import org.eclipse.jdt.core.JavaModelException; 56 import org.eclipse.jdt.core.dom.ASTNode; 57 import org.eclipse.jdt.core.dom.BodyDeclaration; 58 import org.eclipse.jdt.core.dom.CompilationUnit; 59 import org.eclipse.jdt.core.dom.ConstructorInvocation; 60 import org.eclipse.jdt.core.dom.IMethodBinding; 61 import org.eclipse.jdt.core.dom.ITypeBinding; 62 import org.eclipse.jdt.core.dom.MethodDeclaration; 63 import org.eclipse.jdt.core.dom.MethodInvocation; 64 import org.eclipse.jdt.core.dom.Modifier; 65 import org.eclipse.jdt.core.dom.SuperMethodInvocation; 66 import org.eclipse.jdt.core.dom.rewrite.ImportRewrite; 67 import org.eclipse.jdt.core.refactoring.CompilationUnitChange; 68 import org.eclipse.jdt.core.refactoring.descriptors.InlineMethodDescriptor; 69 import org.eclipse.jdt.core.refactoring.descriptors.JavaRefactoringDescriptor; 70 71 import org.eclipse.jdt.internal.core.manipulation.BindingLabelProviderCore; 72 import org.eclipse.jdt.internal.core.manipulation.JavaElementLabelsCore; 73 import org.eclipse.jdt.internal.core.manipulation.util.BasicElementLabels; 74 import org.eclipse.jdt.internal.core.refactoring.descriptors.RefactoringSignatureDescriptorFactory; 75 import org.eclipse.jdt.internal.corext.dom.IASTSharedValues; 76 import org.eclipse.jdt.internal.corext.refactoring.Checks; 77 import org.eclipse.jdt.internal.corext.refactoring.JDTRefactoringDescriptorComment; 78 import org.eclipse.jdt.internal.corext.refactoring.JavaRefactoringDescriptorUtil; 79 import org.eclipse.jdt.internal.corext.refactoring.RefactoringAvailabilityTesterCore; 80 import org.eclipse.jdt.internal.corext.refactoring.RefactoringCoreMessages; 81 import org.eclipse.jdt.internal.corext.refactoring.base.ReferencesInBinaryContext; 82 import org.eclipse.jdt.internal.corext.refactoring.changes.DynamicValidationRefactoringChange; 83 import org.eclipse.jdt.internal.corext.refactoring.changes.TextChangeCompatibility; 84 import org.eclipse.jdt.internal.corext.refactoring.util.JavaElementUtil; 85 import org.eclipse.jdt.internal.corext.refactoring.util.JavaStatusContext; 86 import org.eclipse.jdt.internal.corext.refactoring.util.RefactoringASTParser; 87 import org.eclipse.jdt.internal.corext.refactoring.util.TextChangeManager; 88 import org.eclipse.jdt.internal.corext.util.Messages; 89 90 /* 91 * Open items: 92 * - generate import statements for newly generated local variable declarations. 93 * - forbid cases like foo(foo(10)) when inlining foo(). 94 * - case ref.foo(); and we want to inline foo. Inline a method in a different context; 95 * - optimize code when the method to be inlined returns an argument and that one is 96 * assigned to a parameter again. No need for a separate local (important to be able 97 * to reverse extract method correctly). 98 */ 99 public class InlineMethodRefactoring extends Refactoring { 100 101 private static final String ATTRIBUTE_MODE= "mode"; //$NON-NLS-1$ 102 private static final String ATTRIBUTE_DELETE= "delete"; //$NON-NLS-1$ 103 104 public static class Mode { Mode()105 private Mode() { 106 } 107 public static final Mode INLINE_ALL= new Mode(); 108 public static final Mode INLINE_SINGLE= new Mode(); 109 } 110 111 private ITypeRoot fInitialTypeRoot; 112 private ASTNode fInitialNode; 113 private TextChangeManager fChangeManager; 114 private SourceProvider fSourceProvider; 115 private TargetProvider fTargetProvider; 116 /** 117 * must never be true if fInitialUnit instanceof IClassFile 118 */ 119 private boolean fDeleteSource; 120 private Mode fCurrentMode; 121 private Mode fInitialMode; 122 private int fSelectionStart; 123 private int fSelectionLength; 124 InlineMethodRefactoring(ITypeRoot typeRoot, ASTNode node, int offset, int length)125 private InlineMethodRefactoring(ITypeRoot typeRoot, ASTNode node, int offset, int length) { 126 Assert.isNotNull(typeRoot); 127 Assert.isTrue(JavaElementUtil.isSourceAvailable(typeRoot)); 128 Assert.isNotNull(node); 129 fInitialTypeRoot= typeRoot; 130 fInitialNode= node; 131 fSelectionStart= offset; 132 fSelectionLength= length; 133 } 134 InlineMethodRefactoring(ICompilationUnit unit, MethodInvocation node, int offset, int length)135 private InlineMethodRefactoring(ICompilationUnit unit, MethodInvocation node, int offset, int length) { 136 this(unit, (ASTNode)node, offset, length); 137 fTargetProvider= TargetProvider.create(unit, node); 138 fInitialMode= fCurrentMode= Mode.INLINE_SINGLE; 139 fDeleteSource= false; 140 } 141 InlineMethodRefactoring(ICompilationUnit unit, SuperMethodInvocation node, int offset, int length)142 private InlineMethodRefactoring(ICompilationUnit unit, SuperMethodInvocation node, int offset, int length) { 143 this(unit, (ASTNode)node, offset, length); 144 fTargetProvider= TargetProvider.create(unit, node); 145 fInitialMode= fCurrentMode= Mode.INLINE_SINGLE; 146 fDeleteSource= false; 147 } 148 InlineMethodRefactoring(ICompilationUnit unit, ConstructorInvocation node, int offset, int length)149 private InlineMethodRefactoring(ICompilationUnit unit, ConstructorInvocation node, int offset, int length) { 150 this(unit, (ASTNode)node, offset, length); 151 fTargetProvider= TargetProvider.create(unit, node); 152 fInitialMode= fCurrentMode= Mode.INLINE_SINGLE; 153 fDeleteSource= false; 154 } 155 InlineMethodRefactoring(ITypeRoot typeRoot, MethodDeclaration node, int offset, int length)156 private InlineMethodRefactoring(ITypeRoot typeRoot, MethodDeclaration node, int offset, int length) { 157 this(typeRoot, (ASTNode)node, offset, length); 158 fSourceProvider= new SourceProvider(typeRoot, node); 159 fTargetProvider= TargetProvider.create(node); 160 fInitialMode= fCurrentMode= Mode.INLINE_ALL; 161 fDeleteSource= canEnableDeleteSource(); 162 } 163 164 /** 165 * Creates a new inline method refactoring 166 * @param unit the compilation unit or class file 167 * @param node the compilation unit node 168 * @param selectionStart start 169 * @param selectionLength length 170 * @return returns the refactoring 171 */ create(ITypeRoot unit, CompilationUnit node, int selectionStart, int selectionLength)172 public static InlineMethodRefactoring create(ITypeRoot unit, CompilationUnit node, int selectionStart, int selectionLength) { 173 ASTNode target= RefactoringAvailabilityTesterCore.getInlineableMethodNode(unit, node, selectionStart, selectionLength); 174 if (target == null) 175 return null; 176 if (target.getNodeType() == ASTNode.METHOD_DECLARATION) { 177 178 return new InlineMethodRefactoring(unit, (MethodDeclaration)target, selectionStart, selectionLength); 179 } else { 180 ICompilationUnit cu= (ICompilationUnit) unit; 181 if (target.getNodeType() == ASTNode.METHOD_INVOCATION) { 182 return new InlineMethodRefactoring(cu, (MethodInvocation)target, selectionStart, selectionLength); 183 } else if (target.getNodeType() == ASTNode.SUPER_METHOD_INVOCATION) { 184 return new InlineMethodRefactoring(cu, (SuperMethodInvocation)target, selectionStart, selectionLength); 185 } else if (target.getNodeType() == ASTNode.CONSTRUCTOR_INVOCATION) { 186 return new InlineMethodRefactoring(cu, (ConstructorInvocation)target, selectionStart, selectionLength); 187 } 188 } 189 return null; 190 } 191 192 @Override getName()193 public String getName() { 194 return RefactoringCoreMessages.InlineMethodRefactoring_name; 195 } 196 197 /** 198 * Returns the method to inline, or null if the method could not be found or 199 * {@link #checkInitialConditions(IProgressMonitor)} has not been called yet. 200 * 201 * @return the method, or <code>null</code> 202 */ getMethod()203 public IMethod getMethod() { 204 if (fSourceProvider == null) 205 return null; 206 IMethodBinding binding= fSourceProvider.getDeclaration().resolveBinding(); 207 if (binding == null) 208 return null; 209 return (IMethod) binding.getJavaElement(); 210 } 211 canEnableDeleteSource()212 public boolean canEnableDeleteSource() { 213 return ! (fSourceProvider.getTypeRoot() instanceof IClassFile); 214 } 215 getDeleteSource()216 public boolean getDeleteSource() { 217 return fDeleteSource; 218 } 219 setDeleteSource(boolean remove)220 public void setDeleteSource(boolean remove) { 221 if (remove) 222 Assert.isTrue(canEnableDeleteSource()); 223 fDeleteSource= remove; 224 } 225 getInitialMode()226 public Mode getInitialMode() { 227 return fInitialMode; 228 } 229 setCurrentMode(Mode mode)230 public RefactoringStatus setCurrentMode(Mode mode) throws JavaModelException { 231 if (fCurrentMode == mode) 232 return new RefactoringStatus(); 233 Assert.isTrue(getInitialMode() == Mode.INLINE_SINGLE); 234 fCurrentMode= mode; 235 if (mode == Mode.INLINE_SINGLE) { 236 if (fInitialNode instanceof MethodInvocation) 237 fTargetProvider= TargetProvider.create((ICompilationUnit) fInitialTypeRoot, (MethodInvocation)fInitialNode); 238 else if (fInitialNode instanceof SuperMethodInvocation) 239 fTargetProvider= TargetProvider.create((ICompilationUnit) fInitialTypeRoot, (SuperMethodInvocation)fInitialNode); 240 else if (fInitialNode instanceof ConstructorInvocation) 241 fTargetProvider= TargetProvider.create((ICompilationUnit) fInitialTypeRoot, (ConstructorInvocation)fInitialNode); 242 else 243 throw new IllegalStateException(String.valueOf(fInitialNode)); 244 } else { 245 fTargetProvider= TargetProvider.create(fSourceProvider.getDeclaration()); 246 } 247 return fTargetProvider.checkActivation(); 248 } 249 250 @Override checkInitialConditions(IProgressMonitor pm)251 public RefactoringStatus checkInitialConditions(IProgressMonitor pm) throws CoreException { 252 RefactoringStatus result= new RefactoringStatus(); 253 if (fSourceProvider == null && Invocations.isInvocation(fInitialNode)) { 254 fSourceProvider= resolveSourceProvider(result, fInitialTypeRoot, fInitialNode); 255 if (result.hasFatalError()) 256 return result; 257 } 258 result.merge(fSourceProvider.checkActivation()); 259 result.merge(fTargetProvider.checkActivation()); 260 return result; 261 } 262 263 @Override checkFinalConditions(IProgressMonitor pm)264 public RefactoringStatus checkFinalConditions(IProgressMonitor pm) throws CoreException { 265 pm.beginTask("", 20); //$NON-NLS-1$ 266 fChangeManager= new TextChangeManager(); 267 RefactoringStatus result= new RefactoringStatus(); 268 fSourceProvider.initialize(); 269 fTargetProvider.initialize(); 270 271 pm.setTaskName(RefactoringCoreMessages.InlineMethodRefactoring_searching); 272 RefactoringStatus searchStatus= new RefactoringStatus(); 273 String binaryRefsDescription= Messages.format(RefactoringCoreMessages.ReferencesInBinaryContext_ref_in_binaries_description , BasicElementLabels.getJavaElementName(fSourceProvider.getMethodName())); 274 ReferencesInBinaryContext binaryRefs= new ReferencesInBinaryContext(binaryRefsDescription); 275 ICompilationUnit[] units= fTargetProvider.getAffectedCompilationUnits(searchStatus, binaryRefs, new SubProgressMonitor(pm, 1)); 276 binaryRefs.addErrorIfNecessary(searchStatus); 277 if (searchStatus.hasFatalError()) { 278 result.merge(searchStatus); 279 return result; 280 } 281 282 IFile[] filesToBeModified= getFilesToBeModified(units); 283 result.merge(Checks.validateModifiesFiles(filesToBeModified, getValidationContext(), pm)); 284 if (result.hasFatalError()) 285 return result; 286 result.merge(ResourceChangeChecker.checkFilesToBeChanged(filesToBeModified, new SubProgressMonitor(pm, 1))); 287 checkOverridden(result, new SubProgressMonitor(pm, 4)); 288 IProgressMonitor sub= new SubProgressMonitor(pm, 15); 289 sub.beginTask("", units.length * 3); //$NON-NLS-1$ 290 for (ICompilationUnit unit : units) { 291 sub.subTask(Messages.format(RefactoringCoreMessages.InlineMethodRefactoring_processing, BasicElementLabels.getFileName(unit))); 292 CallInliner inliner= null; 293 try { 294 boolean added= false; 295 MultiTextEdit root= new MultiTextEdit(); 296 CompilationUnitChange change= (CompilationUnitChange)fChangeManager.get(unit); 297 change.setEdit(root); 298 BodyDeclaration[] bodies= fTargetProvider.getAffectedBodyDeclarations(unit, new SubProgressMonitor(pm, 1)); 299 if (bodies.length == 0) 300 continue; 301 inliner= new CallInliner(unit, (CompilationUnit) bodies[0].getRoot(), fSourceProvider); 302 for (BodyDeclaration body : bodies) { 303 inliner.initialize(body); 304 RefactoringStatus nestedInvocations= new RefactoringStatus(); 305 ASTNode[] invocations= removeNestedCalls(nestedInvocations, unit, 306 fTargetProvider.getInvocations(body, new SubProgressMonitor(sub, 2))); 307 for (ASTNode invocation : invocations) { 308 result.merge(inliner.initialize(invocation, fTargetProvider.getStatusSeverity())); 309 if (result.hasFatalError()) 310 break; 311 if (result.getSeverity() < fTargetProvider.getStatusSeverity()) { 312 added= true; 313 TextEditGroup group= new TextEditGroup(RefactoringCoreMessages.InlineMethodRefactoring_edit_inline); 314 change.addTextEditGroup(group); 315 result.merge(inliner.perform(group)); 316 } else { 317 fDeleteSource= false; 318 } 319 } 320 // do this after we have inlined the method calls. We still want 321 // to generate the modifications. 322 if (!nestedInvocations.isOK()) { 323 result.merge(nestedInvocations); 324 fDeleteSource= false; 325 } 326 } 327 if (!added) { 328 fChangeManager.remove(unit); 329 } else { 330 root.addChild(inliner.getModifications()); 331 ImportRewrite rewrite= inliner.getImportEdit(); 332 if (rewrite.hasRecordedChanges()) { 333 TextEdit edit= rewrite.rewriteImports(null); 334 if (edit instanceof MultiTextEdit ? ((MultiTextEdit)edit).getChildrenSize() > 0 : true) { 335 root.addChild(edit); 336 change.addTextEditGroup( 337 new TextEditGroup(RefactoringCoreMessages.InlineMethodRefactoring_edit_import, new TextEdit[] {edit})); 338 } 339 } 340 } 341 } finally { 342 if (inliner != null) 343 inliner.dispose(); 344 } 345 sub.worked(1); 346 if (sub.isCanceled()) 347 throw new OperationCanceledException(); 348 } 349 result.merge(searchStatus); 350 sub.done(); 351 pm.done(); 352 return result; 353 } 354 355 @Override createChange(IProgressMonitor pm)356 public Change createChange(IProgressMonitor pm) throws CoreException { 357 if (fDeleteSource && fCurrentMode == Mode.INLINE_ALL) { 358 TextChange change= fChangeManager.get((ICompilationUnit) fSourceProvider.getTypeRoot()); 359 TextEdit delete= fSourceProvider.getDeleteEdit(); 360 TextEditGroup description= new TextEditGroup( 361 RefactoringCoreMessages.InlineMethodRefactoring_edit_delete, new TextEdit[] { delete }); 362 TextEdit root= change.getEdit(); 363 if (root != null) { 364 // TODO instead of finding the right insert position the call inliner should 365 // reuse the AST & rewriter of the source provide and we should rewrite the 366 // whole AST at the end. However, since recursive calls aren't allowed there 367 // shouldn't be a text edit overlap. 368 // root.addChild(delete); 369 TextChangeCompatibility.insert(root, delete); 370 } else { 371 change.setEdit(delete); 372 } 373 change.addTextEditGroup(description); 374 } 375 final Map<String, String> arguments= new HashMap<>(); 376 String project= null; 377 IJavaProject javaProject= fInitialTypeRoot.getJavaProject(); 378 if (javaProject != null) 379 project= javaProject.getElementName(); 380 int flags= RefactoringDescriptor.STRUCTURAL_CHANGE | JavaRefactoringDescriptor.JAR_REFACTORING | JavaRefactoringDescriptor.JAR_SOURCE_ATTACHMENT; 381 final IMethodBinding binding= fSourceProvider.getDeclaration().resolveBinding(); 382 final ITypeBinding declaring= binding.getDeclaringClass(); 383 if (!Modifier.isPrivate(binding.getModifiers())) 384 flags|= RefactoringDescriptor.MULTI_CHANGE; 385 final String description= Messages.format(RefactoringCoreMessages.InlineMethodRefactoring_descriptor_description_short, BasicElementLabels.getJavaElementName(binding.getName())); 386 final String header= Messages.format(RefactoringCoreMessages.InlineMethodRefactoring_descriptor_description, new String[] { BindingLabelProviderCore.getBindingLabel(binding, JavaElementLabelsCore.ALL_FULLY_QUALIFIED), BindingLabelProviderCore.getBindingLabel(declaring, JavaElementLabelsCore.ALL_FULLY_QUALIFIED)}); 387 final JDTRefactoringDescriptorComment comment= new JDTRefactoringDescriptorComment(project, this, header); 388 comment.addSetting(Messages.format(RefactoringCoreMessages.InlineMethodRefactoring_original_pattern, BindingLabelProviderCore.getBindingLabel(binding, JavaElementLabelsCore.ALL_FULLY_QUALIFIED))); 389 if (fDeleteSource) 390 comment.addSetting(RefactoringCoreMessages.InlineMethodRefactoring_remove_method); 391 if (fCurrentMode == Mode.INLINE_ALL) 392 comment.addSetting(RefactoringCoreMessages.InlineMethodRefactoring_replace_references); 393 final InlineMethodDescriptor descriptor= RefactoringSignatureDescriptorFactory.createInlineMethodDescriptor(project, description, comment.asString(), arguments, flags); 394 arguments.put(JavaRefactoringDescriptorUtil.ATTRIBUTE_INPUT, JavaRefactoringDescriptorUtil.elementToHandle(project, fInitialTypeRoot)); 395 arguments.put(JavaRefactoringDescriptorUtil.ATTRIBUTE_SELECTION, Integer.valueOf(fSelectionStart).toString() + " " + Integer.valueOf(fSelectionLength).toString()); //$NON-NLS-1$ 396 arguments.put(ATTRIBUTE_DELETE, Boolean.valueOf(fDeleteSource).toString()); 397 arguments.put(ATTRIBUTE_MODE, Integer.valueOf(fCurrentMode == Mode.INLINE_ALL ? 1 : 0).toString()); 398 return new DynamicValidationRefactoringChange(descriptor, RefactoringCoreMessages.InlineMethodRefactoring_edit_inlineCall, fChangeManager.getAllChanges()); 399 } 400 resolveSourceProvider(RefactoringStatus status, ITypeRoot typeRoot, ASTNode invocation)401 private static SourceProvider resolveSourceProvider(RefactoringStatus status, ITypeRoot typeRoot, ASTNode invocation) { 402 CompilationUnit root= (CompilationUnit)invocation.getRoot(); 403 IMethodBinding methodBinding= Invocations.resolveBinding(invocation); 404 if (methodBinding == null) { 405 status.addFatalError(RefactoringCoreMessages.InlineMethodRefactoring_error_noMethodDeclaration); 406 return null; 407 } 408 MethodDeclaration declaration= (MethodDeclaration)root.findDeclaringNode(methodBinding); 409 if (declaration != null) { 410 return new SourceProvider(typeRoot, declaration); 411 } 412 IMethod method= (IMethod)methodBinding.getJavaElement(); 413 if (method != null) { 414 CompilationUnit methodDeclarationAstRoot; 415 ICompilationUnit methodCu= method.getCompilationUnit(); 416 if (methodCu != null) { 417 methodDeclarationAstRoot= new RefactoringASTParser(IASTSharedValues.SHARED_AST_LEVEL).parse(methodCu, true); 418 } else { 419 IClassFile classFile= method.getClassFile(); 420 if (! JavaElementUtil.isSourceAvailable(classFile)) { 421 String methodLabel= JavaElementLabelsCore.getTextLabel(method, JavaElementLabelsCore.M_FULLY_QUALIFIED | JavaElementLabelsCore.M_PARAMETER_TYPES); 422 status.addFatalError(Messages.format(RefactoringCoreMessages.InlineMethodRefactoring_error_classFile, methodLabel)); 423 return null; 424 } 425 methodDeclarationAstRoot= new RefactoringASTParser(IASTSharedValues.SHARED_AST_LEVEL).parse(classFile, true); 426 } 427 ASTNode node= methodDeclarationAstRoot.findDeclaringNode(methodBinding.getMethodDeclaration().getKey()); 428 if (node instanceof MethodDeclaration) { 429 return new SourceProvider(methodDeclarationAstRoot.getTypeRoot(), (MethodDeclaration) node); 430 } 431 } 432 status.addFatalError(RefactoringCoreMessages.InlineMethodRefactoring_error_noMethodDeclaration); 433 return null; 434 } 435 getFilesToBeModified(ICompilationUnit[] units)436 private IFile[] getFilesToBeModified(ICompilationUnit[] units) { 437 List<IFile> result= new ArrayList<>(units.length + 1); 438 IFile file; 439 for (ICompilationUnit unit : units) { 440 file= getFile(unit); 441 if (file != null) 442 result.add(file); 443 } 444 if (fDeleteSource) { 445 file= getFile((ICompilationUnit) fSourceProvider.getTypeRoot()); 446 if (file != null && !result.contains(file)) 447 result.add(file); 448 } 449 return result.toArray(new IFile[result.size()]); 450 } 451 getFile(ICompilationUnit unit)452 private IFile getFile(ICompilationUnit unit) { 453 unit= unit.getPrimary(); 454 IResource resource= unit.getResource(); 455 if (resource != null && resource.getType() == IResource.FILE) 456 return (IFile)resource; 457 return null; 458 } 459 checkOverridden(RefactoringStatus status, IProgressMonitor pm)460 private void checkOverridden(RefactoringStatus status, IProgressMonitor pm) throws JavaModelException { 461 pm.beginTask("", 9); //$NON-NLS-1$ 462 pm.setTaskName(RefactoringCoreMessages.InlineMethodRefactoring_checking_overridden); 463 MethodDeclaration decl= fSourceProvider.getDeclaration(); 464 IMethod method= (IMethod) decl.resolveBinding().getJavaElement(); 465 if (method == null || Flags.isPrivate(method.getFlags())) { 466 pm.worked(8); 467 return; 468 } 469 IType type= method.getDeclaringType(); 470 ITypeHierarchy hierarchy= type.newTypeHierarchy(new SubProgressMonitor(pm, 6)); 471 checkSubTypes(status, method, hierarchy.getAllSubtypes(type), new SubProgressMonitor(pm, 1)); 472 checkSuperClasses(status, method, hierarchy.getAllSuperclasses(type), new SubProgressMonitor(pm, 1)); 473 checkSuperInterfaces(status, method, hierarchy.getAllSuperInterfaces(type), new SubProgressMonitor(pm, 1)); 474 pm.setTaskName(""); //$NON-NLS-1$ 475 } 476 checkSubTypes(RefactoringStatus result, IMethod method, IType[] types, IProgressMonitor pm)477 private void checkSubTypes(RefactoringStatus result, IMethod method, IType[] types, IProgressMonitor pm) { 478 checkTypes( 479 result, method, types, 480 RefactoringCoreMessages.InlineMethodRefactoring_checking_overridden_error, 481 pm); 482 } 483 checkSuperClasses(RefactoringStatus result, IMethod method, IType[] types, IProgressMonitor pm)484 private void checkSuperClasses(RefactoringStatus result, IMethod method, IType[] types, IProgressMonitor pm) { 485 checkTypes( 486 result, method, types, 487 RefactoringCoreMessages.InlineMethodRefactoring_checking_overrides_error, 488 pm); 489 } 490 checkSuperInterfaces(RefactoringStatus result, IMethod method, IType[] types, IProgressMonitor pm)491 private void checkSuperInterfaces(RefactoringStatus result, IMethod method, IType[] types, IProgressMonitor pm) { 492 checkTypes( 493 result, method, types, 494 RefactoringCoreMessages.InlineMethodRefactoring_checking_implements_error, 495 pm); 496 } checkTypes(RefactoringStatus result, IMethod method, IType[] types, String key, IProgressMonitor pm)497 private void checkTypes(RefactoringStatus result, IMethod method, IType[] types, String key, IProgressMonitor pm) { 498 pm.beginTask("", types.length); //$NON-NLS-1$ 499 for (IType type : types) { 500 pm.worked(1); 501 IMethod[] overridden= type.findMethods(method); 502 if (overridden != null && overridden.length > 0) { 503 result.addError( 504 Messages.format(key, JavaElementLabelsCore.getElementLabel(type, JavaElementLabelsCore.ALL_DEFAULT)), 505 JavaStatusContext.create(overridden[0])); 506 } 507 } 508 } 509 removeNestedCalls(RefactoringStatus status, ICompilationUnit unit, ASTNode[] invocations)510 private ASTNode[] removeNestedCalls(RefactoringStatus status, ICompilationUnit unit, ASTNode[] invocations) { 511 if (invocations.length <= 1) 512 return invocations; 513 ASTNode[] parents= new ASTNode[invocations.length]; 514 for (int i= 0; i < invocations.length; i++) { 515 parents[i]= invocations[i].getParent(); 516 } 517 for (int i= 0; i < invocations.length; i++) { 518 removeNestedCalls(status, unit, parents, invocations, i); 519 } 520 List<ASTNode> result= new ArrayList<>(); 521 for (ASTNode invocation : invocations) { 522 if (invocation != null) 523 result.add(invocation); 524 } 525 return result.toArray(new ASTNode[result.size()]); 526 } 527 removeNestedCalls(RefactoringStatus status, ICompilationUnit unit, ASTNode[] parents, ASTNode[] invocations, int index)528 private void removeNestedCalls(RefactoringStatus status, ICompilationUnit unit, ASTNode[] parents, ASTNode[] invocations, int index) { 529 ASTNode invocation= invocations[index]; 530 for (ASTNode parent2 : parents) { 531 ASTNode parent= parent2; 532 while (parent != null) { 533 if (parent == invocation) { 534 status.addError(RefactoringCoreMessages.InlineMethodRefactoring_nestedInvocation, 535 JavaStatusContext.create(unit, parent)); 536 invocations[index]= null; 537 } 538 parent= parent.getParent(); 539 } 540 } 541 } 542 543 } 544