replace duplicates when pull methods up

This commit is contained in:
anna
2011-06-20 21:21:07 +04:00
parent 91d3996e1c
commit e32cc3c855
9 changed files with 141 additions and 33 deletions
@@ -1236,6 +1236,11 @@ public class ExtractMethodProcessor implements MatchProvider {
return null;
}
@Override
public String getReplaceDuplicatesTitle(int idx, int size) {
return RefactoringBundle.message("process.duplicates.title", idx, size);
}
public InputVariables getInputVariables() {
return myInputVariables;
}
@@ -24,6 +24,7 @@
*/
package com.intellij.refactoring.memberPullUp;
import com.intellij.analysis.AnalysisScope;
import com.intellij.codeInsight.AnnotationUtil;
import com.intellij.codeInsight.ChangeContextUtil;
import com.intellij.codeInsight.PsiEquivalenceUtil;
@@ -31,10 +32,12 @@ import com.intellij.codeInsight.intention.AddAnnotationFix;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.util.Condition;
import com.intellij.openapi.util.Key;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.*;
import com.intellij.psi.codeStyle.CodeStyleSettings;
import com.intellij.psi.codeStyle.CodeStyleSettingsManager;
import com.intellij.psi.search.LocalSearchScope;
import com.intellij.psi.search.searches.ClassInheritorsSearch;
import com.intellij.psi.search.searches.OverridingMethodsSearch;
import com.intellij.psi.search.searches.ReferencesSearch;
import com.intellij.psi.util.*;
@@ -48,10 +51,12 @@ import com.intellij.refactoring.util.RefactoringUIUtil;
import com.intellij.refactoring.util.RefactoringUtil;
import com.intellij.refactoring.util.classMembers.ClassMemberReferencesVisitor;
import com.intellij.refactoring.util.classMembers.MemberInfo;
import com.intellij.refactoring.util.duplicates.MethodDuplicatesHandler;
import com.intellij.usageView.UsageInfo;
import com.intellij.usageView.UsageViewDescriptor;
import com.intellij.usageView.UsageViewUtil;
import com.intellij.util.IncorrectOperationException;
import com.intellij.util.Query;
import com.intellij.util.VisibilityUtil;
import com.intellij.util.containers.HashMap;
import org.jetbrains.annotations.NotNull;
@@ -94,6 +99,28 @@ public class PullUpHelper extends BaseRefactoringProcessor{
protected void performRefactoring(UsageInfo[] usages) {
moveMembersToBase();
moveFieldInitializations();
processMethodsDuplicates();
}
private void processMethodsDuplicates() {
final Query<PsiClass> search = ClassInheritorsSearch.search(myTargetSuperClass);
final Set<VirtualFile> hierarchyFiles = new HashSet<VirtualFile>();
for (PsiClass aClass : search) {
final PsiFile containingFile = aClass.getContainingFile();
if (containingFile != null) {
final VirtualFile virtualFile = containingFile.getVirtualFile();
if (virtualFile != null) {
hierarchyFiles.add(virtualFile);
}
}
}
final Set<PsiMethod> methodsToSearchDuplicates = new HashSet<PsiMethod>();
for (PsiMember psiMember : myMembersAfterMove) {
if (psiMember instanceof PsiMethod && ((PsiMethod)psiMember).getBody() != null) {
methodsToSearchDuplicates.add((PsiMethod)psiMember);
}
}
MethodDuplicatesHandler.invokeOnScope(myProject, methodsToSearchDuplicates, new AnalysisScope(myProject, hierarchyFiles), true);
}
protected String getCommandName() {
@@ -107,7 +107,7 @@ public class DuplicatesImpl {
if (!ApplicationManager.getApplication().isUnitTestMode()) {
if (showAll.get() == null || !showAll.get()) {
final String prompt = provider.getConfirmDuplicatePrompt(match);
final ReplacePromptDialog promptDialog = new ReplacePromptDialog(false, RefactoringBundle.message("process.duplicates.title", idx, size), project){
final ReplacePromptDialog promptDialog = new ReplacePromptDialog(false, provider.getReplaceDuplicatesTitle(idx, size), project){
@Override
protected String getMessage() {
final String message = super.getMessage();
@@ -33,4 +33,6 @@ public interface MatchProvider {
boolean hasDuplicates();
@Nullable String getConfirmDuplicatePrompt(Match match);
String getReplaceDuplicatesTitle(int idx, int size);
}
@@ -54,9 +54,7 @@ import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.*;
/**
* @author dsl
@@ -109,32 +107,60 @@ public class MethodDuplicatesHandler implements RefactoringActionHandler {
}
public static void invokeOnScope(final Project project, final PsiMethod method, final AnalysisScope scope) {
final List<Match> duplicates = new ArrayList<Match>();
invokeOnScope(project, Collections.singleton(method), scope, false);
}
public static void invokeOnScope(final Project project, final Set<PsiMethod> methods, final AnalysisScope scope, boolean silent) {
final Map<PsiMethod, List<Match>> duplicates = new HashMap<PsiMethod, List<Match>>();
scope.accept(new PsiRecursiveElementVisitor() {
@Override public void visitFile(final PsiFile file) {
final ProgressIndicator progressIndicator = ProgressManager.getInstance().getProgressIndicator();
if (progressIndicator != null && progressIndicator.isCanceled()) return;
duplicates.addAll(hasDuplicates(file, method));
}
});
replaceDuplicate(project, duplicates, method);
final Runnable nothingFoundRunnable = new Runnable() {
public void run() {
if (duplicates.isEmpty()) {
final String message = RefactoringBundle.message("idea.has.not.found.any.code.that.can.be.replaced.with.method.call",
ApplicationNamesInfo.getInstance().getProductName());
Messages.showInfoMessage(project, message, REFACTORING_NAME);
for (PsiMethod method : methods) {
final List<Match> matchList = hasDuplicates(file, method);
for (Iterator<Match> iterator = matchList.iterator(); iterator.hasNext(); ) {
Match match = iterator.next();
final PsiElement matchStart = match.getMatchStart();
final PsiElement matchEnd = match.getMatchEnd();
for (PsiMethod psiMethod : methods) {
if (PsiTreeUtil.isAncestor(psiMethod, matchStart, false) ||
PsiTreeUtil.isAncestor(psiMethod, matchEnd, false)) {
iterator.remove();
break;
}
}
}
if (!matchList.isEmpty()) {
List<Match> matches = duplicates.get(method);
if (matches == null) {
matches = new ArrayList<Match>();
duplicates.put(method, matches);
}
matches.addAll(matchList);
}
}
}
};
if (ApplicationManager.getApplication().isUnitTestMode()) {
nothingFoundRunnable.run();
} else {
ApplicationManager.getApplication().invokeLater(nothingFoundRunnable, ModalityState.NON_MODAL);
});
replaceDuplicate(project, duplicates, methods);
if (!silent) {
final Runnable nothingFoundRunnable = new Runnable() {
public void run() {
if (duplicates.isEmpty()) {
final String message = RefactoringBundle.message("idea.has.not.found.any.code.that.can.be.replaced.with.method.call",
ApplicationNamesInfo.getInstance().getProductName());
Messages.showInfoMessage(project, message, REFACTORING_NAME);
}
}
};
if (ApplicationManager.getApplication().isUnitTestMode()) {
nothingFoundRunnable.run();
} else {
ApplicationManager.getApplication().invokeLater(nothingFoundRunnable, ModalityState.NON_MODAL);
}
}
}
private static void replaceDuplicate(final Project project, final List<Match> duplicates, final PsiMethod method) {
private static void replaceDuplicate(final Project project, final Map<PsiMethod, List<Match>> duplicates, final Set<PsiMethod> methods) {
LocalHistoryAction a = LocalHistory.getInstance().startAction(REFACTORING_NAME);
try {
final ProgressIndicator progressIndicator = ProgressManager.getInstance().getProgressIndicator();
@@ -142,19 +168,23 @@ public class MethodDuplicatesHandler implements RefactoringActionHandler {
final Runnable replaceRunnable = new Runnable() {
public void run() {
final int duplicatesNo = duplicates.size();
WindowManager.getInstance().getStatusBar(project).setInfo(getStatusMessage(duplicatesNo));
CommandProcessor.getInstance().executeCommand(project, new Runnable() {
public void run() {
PostprocessReformattingAspect.getInstance(project).postponeFormattingInside(new Runnable() {
public void run() {
DuplicatesImpl.invoke(project, new MethodDuplicatesMatchProvider(method, duplicates));
}
});
}
}, REFACTORING_NAME, REFACTORING_NAME);
for (final PsiMethod method : methods) {
final List<Match> matches = duplicates.get(method);
if (matches == null) continue;
final int duplicatesNo = matches.size();
WindowManager.getInstance().getStatusBar(project).setInfo(getStatusMessage(duplicatesNo));
CommandProcessor.getInstance().executeCommand(project, new Runnable() {
public void run() {
PostprocessReformattingAspect.getInstance(project).postponeFormattingInside(new Runnable() {
public void run() {
DuplicatesImpl.invoke(project, new MethodDuplicatesMatchProvider(method, matches));
}
});
}
}, REFACTORING_NAME, REFACTORING_NAME);
WindowManager.getInstance().getStatusBar(project).setInfo("");
WindowManager.getInstance().getStatusBar(project).setInfo("");
}
}
};
if (ApplicationManager.getApplication().isUnitTestMode()) {
@@ -348,5 +378,10 @@ public class MethodDuplicatesHandler implements RefactoringActionHandler {
}
return null;
}
@Override
public String getReplaceDuplicatesTitle(int idx, int size) {
return RefactoringBundle.message("process.methods.duplicates.title", idx, size, myMethod.getName());
}
}
}
@@ -0,0 +1,18 @@
class A {}
class AImpl1 extends A{
void f<caret>oo() {
System.out.println("hello");
System.out.println("hello");
System.out.println("hello");
System.out.println("hello");
}
}
class AImpl2 extends A {
void bar() {
System.out.println("hello");
System.out.println("hello");
System.out.println("hello");
System.out.println("hello");
}
}
@@ -0,0 +1,16 @@
class A {
void foo() {
System.out.println("hello");
System.out.println("hello");
System.out.println("hello");
System.out.println("hello");
}
}
class AImpl1 extends A{
}
class AImpl2 extends A {
void bar() {
foo();
}
}
@@ -94,6 +94,10 @@ public class PullUpTest extends LightCodeInsightTestCase {
doTest(new RefactoringTestUtil.MemberDescriptor("method", PsiMethod.class, true));
}
public void testReplaceDuplicatesInInheritors() throws Exception {
doTest(new RefactoringTestUtil.MemberDescriptor("foo", PsiMethod.class, false));
}
public void testGenericsInImplements() throws Exception {
doTest(false, new RefactoringTestUtil.MemberDescriptor("I", PsiClass.class));
}
@@ -623,6 +623,7 @@ replace.this.code.fragment.and.make.method.static.visible= (Method will be made
replace.this.code.fragment.and.make.method.visible= (Method will be made {0})
replace.this.code.fragment.and.change.signature=\nMethod signature will be changed to \n{0}
process.duplicates.title=Process Duplicate {0} of {1}
process.methods.duplicates.title=Process Method {2} Duplicate ({0} of {1})
0.has.detected.1.code.fragments.in.this.file.that.can.be.replaced.with.a.call.to.extracted.method={0} has detected {1} code fragments in this file that can be replaced with a call to extracted method. Would you like to review and replace them?
replace.button=Replace
method.duplicates.method.label=Method {0}