| /******************************************************************************* |
| * Copyright (c) 2000, 2011 IBM Corporation and others. |
| * All rights reserved. This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License v1.0 |
| * which accompanies this distribution, and is available at |
| * http://www.eclipse.org/legal/epl-v10.html |
| * |
| * Contributors: |
| * IBM Corporation - initial API and implementation |
| * Dmitry Stalnov (dstalnov@fusionone.com) - contributed fixes for: |
| * o inline call that is used in a field initializer |
| * (see https://bugs.eclipse.org/bugs/show_bug.cgi?id=38137) |
| * o Allow 'this' constructor to be inlined |
| * (see https://bugs.eclipse.org/bugs/show_bug.cgi?id=38093) |
| *******************************************************************************/ |
| package org.eclipse.jdt.internal.corext.refactoring.code; |
| |
| import java.util.ArrayList; |
| import java.util.HashMap; |
| import java.util.List; |
| import java.util.Map; |
| |
| import org.eclipse.core.runtime.Assert; |
| import org.eclipse.core.runtime.CoreException; |
| import org.eclipse.core.runtime.IProgressMonitor; |
| import org.eclipse.core.runtime.OperationCanceledException; |
| import org.eclipse.core.runtime.SubProgressMonitor; |
| |
| import org.eclipse.core.resources.IFile; |
| import org.eclipse.core.resources.IResource; |
| |
| import org.eclipse.text.edits.MultiTextEdit; |
| import org.eclipse.text.edits.TextEdit; |
| import org.eclipse.text.edits.TextEditGroup; |
| |
| import org.eclipse.ltk.core.refactoring.Change; |
| import org.eclipse.ltk.core.refactoring.Refactoring; |
| import org.eclipse.ltk.core.refactoring.RefactoringDescriptor; |
| import org.eclipse.ltk.core.refactoring.RefactoringStatus; |
| import org.eclipse.ltk.core.refactoring.TextChange; |
| import org.eclipse.ltk.core.refactoring.participants.ResourceChangeChecker; |
| |
| import org.eclipse.jdt.core.Flags; |
| import org.eclipse.jdt.core.IClassFile; |
| import org.eclipse.jdt.core.ICompilationUnit; |
| import org.eclipse.jdt.core.IJavaProject; |
| import org.eclipse.jdt.core.IMethod; |
| import org.eclipse.jdt.core.IType; |
| import org.eclipse.jdt.core.ITypeHierarchy; |
| import org.eclipse.jdt.core.ITypeRoot; |
| import org.eclipse.jdt.core.JavaModelException; |
| import org.eclipse.jdt.core.dom.ASTNode; |
| import org.eclipse.jdt.core.dom.BodyDeclaration; |
| import org.eclipse.jdt.core.dom.CompilationUnit; |
| import org.eclipse.jdt.core.dom.ConstructorInvocation; |
| import org.eclipse.jdt.core.dom.IMethodBinding; |
| import org.eclipse.jdt.core.dom.ITypeBinding; |
| import org.eclipse.jdt.core.dom.MethodDeclaration; |
| import org.eclipse.jdt.core.dom.MethodInvocation; |
| import org.eclipse.jdt.core.dom.Modifier; |
| import org.eclipse.jdt.core.dom.SuperMethodInvocation; |
| import org.eclipse.jdt.core.dom.rewrite.ImportRewrite; |
| import org.eclipse.jdt.core.refactoring.CompilationUnitChange; |
| import org.eclipse.jdt.core.refactoring.descriptors.InlineMethodDescriptor; |
| import org.eclipse.jdt.core.refactoring.descriptors.JavaRefactoringDescriptor; |
| |
| import org.eclipse.jdt.internal.core.refactoring.descriptors.RefactoringSignatureDescriptorFactory; |
| import org.eclipse.jdt.internal.corext.refactoring.Checks; |
| import org.eclipse.jdt.internal.corext.refactoring.JDTRefactoringDescriptorComment; |
| import org.eclipse.jdt.internal.corext.refactoring.JavaRefactoringDescriptorUtil; |
| import org.eclipse.jdt.internal.corext.refactoring.RefactoringAvailabilityTester; |
| import org.eclipse.jdt.internal.corext.refactoring.RefactoringCoreMessages; |
| import org.eclipse.jdt.internal.corext.refactoring.base.JavaStatusContext; |
| import org.eclipse.jdt.internal.corext.refactoring.base.ReferencesInBinaryContext; |
| import org.eclipse.jdt.internal.corext.refactoring.changes.DynamicValidationRefactoringChange; |
| import org.eclipse.jdt.internal.corext.refactoring.changes.TextChangeCompatibility; |
| import org.eclipse.jdt.internal.corext.refactoring.util.JavaElementUtil; |
| import org.eclipse.jdt.internal.corext.refactoring.util.RefactoringASTParser; |
| import org.eclipse.jdt.internal.corext.refactoring.util.TextChangeManager; |
| import org.eclipse.jdt.internal.corext.util.Messages; |
| |
| import org.eclipse.jdt.ui.JavaElementLabels; |
| |
| import org.eclipse.jdt.internal.ui.javaeditor.ASTProvider; |
| import org.eclipse.jdt.internal.ui.viewsupport.BasicElementLabels; |
| import org.eclipse.jdt.internal.ui.viewsupport.BindingLabelProvider; |
| |
| /* |
| * Open items: |
| * - generate import statements for newly generated local variable declarations. |
| * - forbid cases like foo(foo(10)) when inlining foo(). |
| * - case ref.foo(); and we want to inline foo. Inline a method in a different context; |
| * - optimize code when the method to be inlined returns an argument and that one is |
| * assigned to a parameter again. No need for a separate local (important to be able |
| * to reverse extract method correctly). |
| */ |
| public class InlineMethodRefactoring extends Refactoring { |
| |
| private static final String ATTRIBUTE_MODE= "mode"; //$NON-NLS-1$ |
| private static final String ATTRIBUTE_DELETE= "delete"; //$NON-NLS-1$ |
| |
| public static class Mode { |
| private Mode() { |
| } |
| public static final Mode INLINE_ALL= new Mode(); |
| public static final Mode INLINE_SINGLE= new Mode(); |
| } |
| |
| private ITypeRoot fInitialTypeRoot; |
| private ASTNode fInitialNode; |
| private TextChangeManager fChangeManager; |
| private SourceProvider fSourceProvider; |
| private TargetProvider fTargetProvider; |
| /** |
| * must never be true if fInitialUnit instanceof IClassFile |
| */ |
| private boolean fDeleteSource; |
| private Mode fCurrentMode; |
| private Mode fInitialMode; |
| private int fSelectionStart; |
| private int fSelectionLength; |
| |
| private InlineMethodRefactoring(ITypeRoot typeRoot, ASTNode node, int offset, int length) { |
| Assert.isNotNull(typeRoot); |
| Assert.isTrue(JavaElementUtil.isSourceAvailable(typeRoot)); |
| Assert.isNotNull(node); |
| fInitialTypeRoot= typeRoot; |
| fInitialNode= node; |
| fSelectionStart= offset; |
| fSelectionLength= length; |
| } |
| |
| private InlineMethodRefactoring(ICompilationUnit unit, MethodInvocation node, int offset, int length) { |
| this(unit, (ASTNode)node, offset, length); |
| fTargetProvider= TargetProvider.create(unit, node); |
| fInitialMode= fCurrentMode= Mode.INLINE_SINGLE; |
| fDeleteSource= false; |
| } |
| |
| private InlineMethodRefactoring(ICompilationUnit unit, SuperMethodInvocation node, int offset, int length) { |
| this(unit, (ASTNode)node, offset, length); |
| fTargetProvider= TargetProvider.create(unit, node); |
| fInitialMode= fCurrentMode= Mode.INLINE_SINGLE; |
| fDeleteSource= false; |
| } |
| |
| private InlineMethodRefactoring(ICompilationUnit unit, ConstructorInvocation node, int offset, int length) { |
| this(unit, (ASTNode)node, offset, length); |
| fTargetProvider= TargetProvider.create(unit, node); |
| fInitialMode= fCurrentMode= Mode.INLINE_SINGLE; |
| fDeleteSource= false; |
| } |
| |
| private InlineMethodRefactoring(ITypeRoot typeRoot, MethodDeclaration node, int offset, int length) { |
| this(typeRoot, (ASTNode)node, offset, length); |
| fSourceProvider= new SourceProvider(typeRoot, node); |
| fTargetProvider= TargetProvider.create(node); |
| fInitialMode= fCurrentMode= Mode.INLINE_ALL; |
| fDeleteSource= canEnableDeleteSource(); |
| } |
| |
| /** |
| * Creates a new inline method refactoring |
| * @param unit the compilation unit or class file |
| * @param node the compilation unit node |
| * @param selectionStart start |
| * @param selectionLength length |
| * @return returns the refactoring |
| */ |
| public static InlineMethodRefactoring create(ITypeRoot unit, CompilationUnit node, int selectionStart, int selectionLength) { |
| ASTNode target= RefactoringAvailabilityTester.getInlineableMethodNode(unit, node, selectionStart, selectionLength); |
| if (target == null) |
| return null; |
| if (target.getNodeType() == ASTNode.METHOD_DECLARATION) { |
| |
| return new InlineMethodRefactoring(unit, (MethodDeclaration)target, selectionStart, selectionLength); |
| } else { |
| ICompilationUnit cu= (ICompilationUnit) unit; |
| if (target.getNodeType() == ASTNode.METHOD_INVOCATION) { |
| return new InlineMethodRefactoring(cu, (MethodInvocation)target, selectionStart, selectionLength); |
| } else if (target.getNodeType() == ASTNode.SUPER_METHOD_INVOCATION) { |
| return new InlineMethodRefactoring(cu, (SuperMethodInvocation)target, selectionStart, selectionLength); |
| } else if (target.getNodeType() == ASTNode.CONSTRUCTOR_INVOCATION) { |
| return new InlineMethodRefactoring(cu, (ConstructorInvocation)target, selectionStart, selectionLength); |
| } |
| } |
| return null; |
| } |
| |
| @Override |
| public String getName() { |
| return RefactoringCoreMessages.InlineMethodRefactoring_name; |
| } |
| |
| /** |
| * Returns the method to inline, or null if the method could not be found or |
| * {@link #checkInitialConditions(IProgressMonitor)} has not been called yet. |
| * |
| * @return the method, or <code>null</code> |
| */ |
| public IMethod getMethod() { |
| if (fSourceProvider == null) |
| return null; |
| IMethodBinding binding= fSourceProvider.getDeclaration().resolveBinding(); |
| if (binding == null) |
| return null; |
| return (IMethod) binding.getJavaElement(); |
| } |
| |
| public boolean canEnableDeleteSource() { |
| return ! (fSourceProvider.getTypeRoot() instanceof IClassFile); |
| } |
| |
| public boolean getDeleteSource() { |
| return fDeleteSource; |
| } |
| |
| public void setDeleteSource(boolean remove) { |
| if (remove) |
| Assert.isTrue(canEnableDeleteSource()); |
| fDeleteSource= remove; |
| } |
| |
| public Mode getInitialMode() { |
| return fInitialMode; |
| } |
| |
| public RefactoringStatus setCurrentMode(Mode mode) throws JavaModelException { |
| if (fCurrentMode == mode) |
| return new RefactoringStatus(); |
| Assert.isTrue(getInitialMode() == Mode.INLINE_SINGLE); |
| fCurrentMode= mode; |
| if (mode == Mode.INLINE_SINGLE) { |
| if (fInitialNode instanceof MethodInvocation) |
| fTargetProvider= TargetProvider.create((ICompilationUnit) fInitialTypeRoot, (MethodInvocation)fInitialNode); |
| else if (fInitialNode instanceof SuperMethodInvocation) |
| fTargetProvider= TargetProvider.create((ICompilationUnit) fInitialTypeRoot, (SuperMethodInvocation)fInitialNode); |
| else if (fInitialNode instanceof ConstructorInvocation) |
| fTargetProvider= TargetProvider.create((ICompilationUnit) fInitialTypeRoot, (ConstructorInvocation)fInitialNode); |
| else |
| throw new IllegalStateException(String.valueOf(fInitialNode)); |
| } else { |
| fTargetProvider= TargetProvider.create(fSourceProvider.getDeclaration()); |
| } |
| return fTargetProvider.checkActivation(); |
| } |
| |
| @Override |
| public RefactoringStatus checkInitialConditions(IProgressMonitor pm) throws CoreException { |
| RefactoringStatus result= new RefactoringStatus(); |
| if (fSourceProvider == null && Invocations.isInvocation(fInitialNode)) { |
| fSourceProvider= resolveSourceProvider(result, fInitialTypeRoot, fInitialNode); |
| if (result.hasFatalError()) |
| return result; |
| } |
| result.merge(fSourceProvider.checkActivation()); |
| result.merge(fTargetProvider.checkActivation()); |
| return result; |
| } |
| |
| @Override |
| public RefactoringStatus checkFinalConditions(IProgressMonitor pm) throws CoreException { |
| pm.beginTask("", 20); //$NON-NLS-1$ |
| fChangeManager= new TextChangeManager(); |
| RefactoringStatus result= new RefactoringStatus(); |
| fSourceProvider.initialize(); |
| fTargetProvider.initialize(); |
| |
| pm.setTaskName(RefactoringCoreMessages.InlineMethodRefactoring_searching); |
| RefactoringStatus searchStatus= new RefactoringStatus(); |
| String binaryRefsDescription= Messages.format(RefactoringCoreMessages.ReferencesInBinaryContext_ref_in_binaries_description , BasicElementLabels.getJavaElementName(fSourceProvider.getMethodName())); |
| ReferencesInBinaryContext binaryRefs= new ReferencesInBinaryContext(binaryRefsDescription); |
| ICompilationUnit[] units= fTargetProvider.getAffectedCompilationUnits(searchStatus, binaryRefs, new SubProgressMonitor(pm, 1)); |
| binaryRefs.addErrorIfNecessary(searchStatus); |
| if (searchStatus.hasFatalError()) { |
| result.merge(searchStatus); |
| return result; |
| } |
| |
| IFile[] filesToBeModified= getFilesToBeModified(units); |
| result.merge(Checks.validateModifiesFiles(filesToBeModified, getValidationContext())); |
| if (result.hasFatalError()) |
| return result; |
| result.merge(ResourceChangeChecker.checkFilesToBeChanged(filesToBeModified, new SubProgressMonitor(pm, 1))); |
| checkOverridden(result, new SubProgressMonitor(pm, 4)); |
| IProgressMonitor sub= new SubProgressMonitor(pm, 15); |
| sub.beginTask("", units.length * 3); //$NON-NLS-1$ |
| for (int c= 0; c < units.length; c++) { |
| ICompilationUnit unit= units[c]; |
| sub.subTask(Messages.format(RefactoringCoreMessages.InlineMethodRefactoring_processing, BasicElementLabels.getFileName(unit))); |
| CallInliner inliner= null; |
| try { |
| boolean added= false; |
| MultiTextEdit root= new MultiTextEdit(); |
| CompilationUnitChange change= (CompilationUnitChange)fChangeManager.get(unit); |
| change.setEdit(root); |
| BodyDeclaration[] bodies= fTargetProvider.getAffectedBodyDeclarations(unit, new SubProgressMonitor(pm, 1)); |
| if (bodies.length == 0) |
| continue; |
| inliner= new CallInliner(unit, (CompilationUnit) bodies[0].getRoot(), fSourceProvider); |
| for (int b= 0; b < bodies.length; b++) { |
| BodyDeclaration body= bodies[b]; |
| inliner.initialize(body); |
| RefactoringStatus nestedInvocations= new RefactoringStatus(); |
| ASTNode[] invocations= removeNestedCalls(nestedInvocations, unit, |
| fTargetProvider.getInvocations(body, new SubProgressMonitor(sub, 2))); |
| for (int i= 0; i < invocations.length; i++) { |
| ASTNode invocation= invocations[i]; |
| result.merge(inliner.initialize(invocation, fTargetProvider.getStatusSeverity())); |
| if (result.hasFatalError()) |
| break; |
| if (result.getSeverity() < fTargetProvider.getStatusSeverity()) { |
| added= true; |
| TextEditGroup group= new TextEditGroup(RefactoringCoreMessages.InlineMethodRefactoring_edit_inline); |
| change.addTextEditGroup(group); |
| result.merge(inliner.perform(group)); |
| } else { |
| fDeleteSource= false; |
| } |
| } |
| // do this after we have inlined the method calls. We still want |
| // to generate the modifications. |
| if (!nestedInvocations.isOK()) { |
| result.merge(nestedInvocations); |
| fDeleteSource= false; |
| } |
| } |
| if (!added) { |
| fChangeManager.remove(unit); |
| } else { |
| root.addChild(inliner.getModifications()); |
| ImportRewrite rewrite= inliner.getImportEdit(); |
| if (rewrite.hasRecordedChanges()) { |
| TextEdit edit= rewrite.rewriteImports(null); |
| if (edit instanceof MultiTextEdit ? ((MultiTextEdit)edit).getChildrenSize() > 0 : true) { |
| root.addChild(edit); |
| change.addTextEditGroup( |
| new TextEditGroup(RefactoringCoreMessages.InlineMethodRefactoring_edit_import, new TextEdit[] {edit})); |
| } |
| } |
| } |
| } finally { |
| if (inliner != null) |
| inliner.dispose(); |
| } |
| sub.worked(1); |
| if (sub.isCanceled()) |
| throw new OperationCanceledException(); |
| } |
| result.merge(searchStatus); |
| sub.done(); |
| pm.done(); |
| return result; |
| } |
| |
| @Override |
| public Change createChange(IProgressMonitor pm) throws CoreException { |
| if (fDeleteSource && fCurrentMode == Mode.INLINE_ALL) { |
| TextChange change= fChangeManager.get((ICompilationUnit) fSourceProvider.getTypeRoot()); |
| TextEdit delete= fSourceProvider.getDeleteEdit(); |
| TextEditGroup description= new TextEditGroup( |
| RefactoringCoreMessages.InlineMethodRefactoring_edit_delete, new TextEdit[] { delete }); |
| TextEdit root= change.getEdit(); |
| if (root != null) { |
| // TODO instead of finding the right insert position the call inliner should |
| // reuse the AST & rewriter of the source provide and we should rewrite the |
| // whole AST at the end. However, since recursive calls aren't allowed there |
| // shouldn't be a text edit overlap. |
| // root.addChild(delete); |
| TextChangeCompatibility.insert(root, delete); |
| } else { |
| change.setEdit(delete); |
| } |
| change.addTextEditGroup(description); |
| } |
| final Map<String, String> arguments= new HashMap<String, String>(); |
| String project= null; |
| IJavaProject javaProject= fInitialTypeRoot.getJavaProject(); |
| if (javaProject != null) |
| project= javaProject.getElementName(); |
| int flags= RefactoringDescriptor.STRUCTURAL_CHANGE | JavaRefactoringDescriptor.JAR_REFACTORING | JavaRefactoringDescriptor.JAR_SOURCE_ATTACHMENT; |
| final IMethodBinding binding= fSourceProvider.getDeclaration().resolveBinding(); |
| final ITypeBinding declaring= binding.getDeclaringClass(); |
| if (!Modifier.isPrivate(binding.getModifiers())) |
| flags|= RefactoringDescriptor.MULTI_CHANGE; |
| final String description= Messages.format(RefactoringCoreMessages.InlineMethodRefactoring_descriptor_description_short, BasicElementLabels.getJavaElementName(binding.getName())); |
| final String header= Messages.format(RefactoringCoreMessages.InlineMethodRefactoring_descriptor_description, new String[] { BindingLabelProvider.getBindingLabel(binding, JavaElementLabels.ALL_FULLY_QUALIFIED), BindingLabelProvider.getBindingLabel(declaring, JavaElementLabels.ALL_FULLY_QUALIFIED)}); |
| final JDTRefactoringDescriptorComment comment= new JDTRefactoringDescriptorComment(project, this, header); |
| comment.addSetting(Messages.format(RefactoringCoreMessages.InlineMethodRefactoring_original_pattern, BindingLabelProvider.getBindingLabel(binding, JavaElementLabels.ALL_FULLY_QUALIFIED))); |
| if (fDeleteSource) |
| comment.addSetting(RefactoringCoreMessages.InlineMethodRefactoring_remove_method); |
| if (fCurrentMode == Mode.INLINE_ALL) |
| comment.addSetting(RefactoringCoreMessages.InlineMethodRefactoring_replace_references); |
| final InlineMethodDescriptor descriptor= RefactoringSignatureDescriptorFactory.createInlineMethodDescriptor(project, description, comment.asString(), arguments, flags); |
| arguments.put(JavaRefactoringDescriptorUtil.ATTRIBUTE_INPUT, JavaRefactoringDescriptorUtil.elementToHandle(project, fInitialTypeRoot)); |
| arguments.put(JavaRefactoringDescriptorUtil.ATTRIBUTE_SELECTION, new Integer(fSelectionStart).toString() + " " + new Integer(fSelectionLength).toString()); //$NON-NLS-1$ |
| arguments.put(ATTRIBUTE_DELETE, Boolean.valueOf(fDeleteSource).toString()); |
| arguments.put(ATTRIBUTE_MODE, new Integer(fCurrentMode == Mode.INLINE_ALL ? 1 : 0).toString()); |
| return new DynamicValidationRefactoringChange(descriptor, RefactoringCoreMessages.InlineMethodRefactoring_edit_inlineCall, fChangeManager.getAllChanges()); |
| } |
| |
| private static SourceProvider resolveSourceProvider(RefactoringStatus status, ITypeRoot typeRoot, ASTNode invocation) { |
| CompilationUnit root= (CompilationUnit)invocation.getRoot(); |
| IMethodBinding methodBinding= Invocations.resolveBinding(invocation); |
| if (methodBinding == null) { |
| status.addFatalError(RefactoringCoreMessages.InlineMethodRefactoring_error_noMethodDeclaration); |
| return null; |
| } |
| MethodDeclaration declaration= (MethodDeclaration)root.findDeclaringNode(methodBinding); |
| if (declaration != null) { |
| return new SourceProvider(typeRoot, declaration); |
| } |
| IMethod method= (IMethod)methodBinding.getJavaElement(); |
| if (method != null) { |
| CompilationUnit methodDeclarationAstRoot; |
| ICompilationUnit methodCu= method.getCompilationUnit(); |
| if (methodCu != null) { |
| methodDeclarationAstRoot= new RefactoringASTParser(ASTProvider.SHARED_AST_LEVEL).parse(methodCu, true); |
| } else { |
| IClassFile classFile= method.getClassFile(); |
| if (! JavaElementUtil.isSourceAvailable(classFile)) { |
| String methodLabel= JavaElementLabels.getTextLabel(method, JavaElementLabels.M_FULLY_QUALIFIED | JavaElementLabels.M_PARAMETER_TYPES); |
| status.addFatalError(Messages.format(RefactoringCoreMessages.InlineMethodRefactoring_error_classFile, methodLabel)); |
| return null; |
| } |
| methodDeclarationAstRoot= new RefactoringASTParser(ASTProvider.SHARED_AST_LEVEL).parse(classFile, true); |
| } |
| ASTNode node= methodDeclarationAstRoot.findDeclaringNode(methodBinding.getMethodDeclaration().getKey()); |
| if (node instanceof MethodDeclaration) { |
| return new SourceProvider(methodDeclarationAstRoot.getTypeRoot(), (MethodDeclaration) node); |
| } |
| } |
| status.addFatalError(RefactoringCoreMessages.InlineMethodRefactoring_error_noMethodDeclaration); |
| return null; |
| } |
| |
| private IFile[] getFilesToBeModified(ICompilationUnit[] units) { |
| List<IFile> result= new ArrayList<IFile>(units.length + 1); |
| IFile file; |
| for (int i= 0; i < units.length; i++) { |
| file= getFile(units[i]); |
| if (file != null) |
| result.add(file); |
| } |
| if (fDeleteSource) { |
| file= getFile((ICompilationUnit) fSourceProvider.getTypeRoot()); |
| if (file != null && !result.contains(file)) |
| result.add(file); |
| } |
| return result.toArray(new IFile[result.size()]); |
| } |
| |
| private IFile getFile(ICompilationUnit unit) { |
| unit= unit.getPrimary(); |
| IResource resource= unit.getResource(); |
| if (resource != null && resource.getType() == IResource.FILE) |
| return (IFile)resource; |
| return null; |
| } |
| |
| private void checkOverridden(RefactoringStatus status, IProgressMonitor pm) throws JavaModelException { |
| pm.beginTask("", 9); //$NON-NLS-1$ |
| pm.setTaskName(RefactoringCoreMessages.InlineMethodRefactoring_checking_overridden); |
| MethodDeclaration decl= fSourceProvider.getDeclaration(); |
| IMethod method= (IMethod) decl.resolveBinding().getJavaElement(); |
| if (method == null || Flags.isPrivate(method.getFlags())) { |
| pm.worked(8); |
| return; |
| } |
| IType type= method.getDeclaringType(); |
| ITypeHierarchy hierarchy= type.newTypeHierarchy(new SubProgressMonitor(pm, 6)); |
| checkSubTypes(status, method, hierarchy.getAllSubtypes(type), new SubProgressMonitor(pm, 1)); |
| checkSuperClasses(status, method, hierarchy.getAllSuperclasses(type), new SubProgressMonitor(pm, 1)); |
| checkSuperInterfaces(status, method, hierarchy.getAllSuperInterfaces(type), new SubProgressMonitor(pm, 1)); |
| pm.setTaskName(""); //$NON-NLS-1$ |
| } |
| |
| private void checkSubTypes(RefactoringStatus result, IMethod method, IType[] types, IProgressMonitor pm) { |
| checkTypes( |
| result, method, types, |
| RefactoringCoreMessages.InlineMethodRefactoring_checking_overridden_error, |
| pm); |
| } |
| |
| private void checkSuperClasses(RefactoringStatus result, IMethod method, IType[] types, IProgressMonitor pm) { |
| checkTypes( |
| result, method, types, |
| RefactoringCoreMessages.InlineMethodRefactoring_checking_overrides_error, |
| pm); |
| } |
| |
| private void checkSuperInterfaces(RefactoringStatus result, IMethod method, IType[] types, IProgressMonitor pm) { |
| checkTypes( |
| result, method, types, |
| RefactoringCoreMessages.InlineMethodRefactoring_checking_implements_error, |
| pm); |
| } |
| private void checkTypes(RefactoringStatus result, IMethod method, IType[] types, String key, IProgressMonitor pm) { |
| pm.beginTask("", types.length); //$NON-NLS-1$ |
| for (int i= 0; i < types.length; i++) { |
| pm.worked(1); |
| IMethod[] overridden= types[i].findMethods(method); |
| if (overridden != null && overridden.length > 0) { |
| result.addError( |
| Messages.format(key, JavaElementLabels.getElementLabel(types[i], JavaElementLabels.ALL_DEFAULT)), |
| JavaStatusContext.create(overridden[0])); |
| } |
| } |
| } |
| |
| private ASTNode[] removeNestedCalls(RefactoringStatus status, ICompilationUnit unit, ASTNode[] invocations) { |
| if (invocations.length <= 1) |
| return invocations; |
| ASTNode[] parents= new ASTNode[invocations.length]; |
| for (int i= 0; i < invocations.length; i++) { |
| parents[i]= invocations[i].getParent(); |
| } |
| for (int i= 0; i < invocations.length; i++) { |
| removeNestedCalls(status, unit, parents, invocations, i); |
| } |
| List<ASTNode> result= new ArrayList<ASTNode>(); |
| for (int i= 0; i < invocations.length; i++) { |
| if (invocations[i] != null) |
| result.add(invocations[i]); |
| } |
| return result.toArray(new ASTNode[result.size()]); |
| } |
| |
| private void removeNestedCalls(RefactoringStatus status, ICompilationUnit unit, ASTNode[] parents, ASTNode[] invocations, int index) { |
| ASTNode invocation= invocations[index]; |
| for (int i= 0; i < parents.length; i++) { |
| ASTNode parent= parents[i]; |
| while (parent != null) { |
| if (parent == invocation) { |
| status.addError(RefactoringCoreMessages.InlineMethodRefactoring_nestedInvocation, |
| JavaStatusContext.create(unit, parent)); |
| invocations[index]= null; |
| } |
| parent= parent.getParent(); |
| } |
| } |
| } |
| |
| } |