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