replace constant duplicates (IDEA-43784)
authoranna <Anna.Kozlova@jetbrains.com>
Mon, 16 Jan 2012 18:50:58 +0000 (19:50 +0100)
committeranna <Anna.Kozlova@jetbrains.com>
Mon, 16 Jan 2012 19:12:12 +0000 (20:12 +0100)
13 files changed:
java/java-impl/src/com/intellij/refactoring/memberPullUp/PullUpHelper.java
java/java-impl/src/com/intellij/refactoring/util/duplicates/ConstantMatchProvider.java [new file with mode: 0644]
java/java-impl/src/com/intellij/refactoring/util/duplicates/Match.java
java/java-impl/src/com/intellij/refactoring/util/duplicates/MethodDuplicatesHandler.java
java/java-impl/src/com/intellij/refactoring/util/duplicates/MethodDuplicatesMatchProvider.java [new file with mode: 0644]
java/java-tests/testData/refactoring/methodDuplicatesMisc/AnonymousInitializer.java [new file with mode: 0644]
java/java-tests/testData/refactoring/methodDuplicatesMisc/AnonymousInitializer.java.after [new file with mode: 0644]
java/java-tests/testData/refactoring/methodDuplicatesMisc/SimpleConstant.java [new file with mode: 0644]
java/java-tests/testData/refactoring/methodDuplicatesMisc/SimpleConstant.java.after [new file with mode: 0644]
java/java-tests/testSrc/com/intellij/refactoring/FindMethodDuplicatesBaseTest.java
java/java-tests/testSrc/com/intellij/refactoring/FindMethodDuplicatesMiscTest.java
platform/platform-resources-en/src/messages/ActionsBundle.properties
platform/platform-resources-en/src/messages/RefactoringBundle.properties

index f0671562414713e16703f831db4488eaaf97c70d..aa7f8d617d00adfc8d04b684c9b0c228e00e679f 100644 (file)
@@ -145,7 +145,7 @@ public class PullUpHelper extends BaseRefactoringProcessor{
             }
           }
         }
-        final Set<PsiMethod> methodsToSearchDuplicates = new HashSet<PsiMethod>();
+        final Set<PsiMember> methodsToSearchDuplicates = new HashSet<PsiMember>();
         for (PsiMember psiMember : myMembersAfterMove) {
           if (psiMember instanceof PsiMethod && ((PsiMethod)psiMember).getBody() != null) {
             methodsToSearchDuplicates.add((PsiMethod)psiMember);
diff --git a/java/java-impl/src/com/intellij/refactoring/util/duplicates/ConstantMatchProvider.java b/java/java-impl/src/com/intellij/refactoring/util/duplicates/ConstantMatchProvider.java
new file mode 100644 (file)
index 0000000..482cb64
--- /dev/null
@@ -0,0 +1,74 @@
+/*
+ * Copyright 2000-2012 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.intellij.refactoring.util.duplicates;
+
+import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.openapi.project.Project;
+import com.intellij.psi.*;
+import com.intellij.psi.util.PsiTreeUtil;
+import com.intellij.refactoring.RefactoringBundle;
+import com.intellij.util.IncorrectOperationException;
+
+import java.util.List;
+
+/**
+* User: anna
+* Date: 1/16/12
+*/
+class ConstantMatchProvider implements MatchProvider {
+  private final PsiField myField;
+  private final Project myProject;
+  private final List<Match> myMatches;
+  private static final Logger LOG = Logger.getInstance("#" + ConstantMatchProvider.class.getName());
+
+  public ConstantMatchProvider(PsiMember member, Project project, List<Match> matches) {
+    myField = (PsiField)member;
+    myProject = project;
+    myMatches = matches;
+  }
+
+  @Override
+  public PsiElement processMatch(Match match) throws IncorrectOperationException {
+    final PsiClass containingClass = myField.getContainingClass();
+    LOG.assertTrue(containingClass != null, myField);
+    String fieldReference = myField.getName();
+    final PsiElement start = match.getMatchStart();
+    if (!PsiTreeUtil.isAncestor(containingClass, start, false)) {
+      fieldReference = containingClass.getQualifiedName() + "." + fieldReference; 
+    }
+    return match.replaceWithExpression(JavaPsiFacade.getElementFactory(myProject).createExpressionFromText(fieldReference, myField));
+  }
+
+  @Override
+  public List<Match> getDuplicates() {
+    return myMatches;
+  }
+
+  @Override
+  public boolean hasDuplicates() {
+    return !myMatches.isEmpty();
+  }
+
+  @Override
+  public String getConfirmDuplicatePrompt(Match match) {
+    return null;
+  }
+
+  @Override
+  public String getReplaceDuplicatesTitle(int idx, int size) {
+    return RefactoringBundle.message("process.duplicates.title", idx, size);
+  }
+}
index 933854bbb11bfe27b21b5999cee69cdd67620e0b..8f10497c3288388af847e847f3f541587b0bf214 100644 (file)
@@ -307,11 +307,13 @@ public final class Match {
     }
   }
 
-  private PsiElement replaceWithExpression(final PsiMethodCallExpression methodCallExpression) throws IncorrectOperationException {
+  public PsiElement replaceWithExpression(final PsiExpression psiExpression) throws IncorrectOperationException {
     final PsiElement matchStart = getMatchStart();
     LOG.assertTrue(matchStart == getMatchEnd());
-    if (matchStart instanceof PsiReferenceExpression && matchStart.getParent() instanceof PsiMethodCallExpression) return matchStart.replace(methodCallExpression.getMethodExpression());
-    return matchStart.replace(methodCallExpression);
+    if (psiExpression instanceof PsiMethodCallExpression && matchStart instanceof PsiReferenceExpression && matchStart.getParent() instanceof PsiMethodCallExpression) {
+      return matchStart.replace(((PsiMethodCallExpression)psiExpression).getMethodExpression());
+    }
+    return matchStart.replace(psiExpression);
   }
 
   TextRange getTextRange() {
index 2a90270b12a3e4c0ce6b7d0aebd254d3d61ca8da..30baca424df5b18422588c10d8c3789902047b13 100644 (file)
@@ -35,22 +35,14 @@ import com.intellij.openapi.project.Project;
 import com.intellij.openapi.ui.Messages;
 import com.intellij.openapi.wm.WindowManager;
 import com.intellij.psi.*;
-import com.intellij.psi.codeStyle.CodeStyleManager;
 import com.intellij.psi.impl.source.PostprocessReformattingAspect;
 import com.intellij.psi.search.LocalSearchScope;
-import com.intellij.psi.util.InheritanceUtil;
 import com.intellij.psi.util.PsiTreeUtil;
-import com.intellij.psi.util.PsiTypesUtil;
-import com.intellij.psi.util.PsiUtil;
 import com.intellij.refactoring.HelpID;
 import com.intellij.refactoring.RefactoringActionHandler;
 import com.intellij.refactoring.RefactoringBundle;
 import com.intellij.refactoring.extractMethod.InputVariables;
 import com.intellij.refactoring.util.CommonRefactoringUtil;
-import com.intellij.refactoring.util.RefactoringUtil;
-import com.intellij.util.IncorrectOperationException;
-import com.intellij.util.VisibilityUtil;
-import org.jetbrains.annotations.NonNls;
 import org.jetbrains.annotations.NotNull;
 import org.jetbrains.annotations.Nullable;
 
@@ -67,29 +59,14 @@ public class MethodDuplicatesHandler implements RefactoringActionHandler {
   public void invoke(@NotNull final Project project, final Editor editor, PsiFile file, DataContext dataContext) {
     final int offset = editor.getCaretModel().getOffset();
     final PsiElement element = file.findElementAt(offset);
-    final PsiMethod method = PsiTreeUtil.getParentOfType(element, PsiMethod.class);
-    if (method == null) {
-      String message = RefactoringBundle.getCannotRefactorMessage(RefactoringBundle.message("locate.caret.inside.a.method"));
+    final PsiMember member = PsiTreeUtil.getParentOfType(element, PsiMember.class);
+    final String cannotRefactorMessage = getCannotRefactorMessage(member);
+    if (cannotRefactorMessage != null) {
+      String message = RefactoringBundle.getCannotRefactorMessage(cannotRefactorMessage);
       showErrorMessage(message, project, editor);
       return;
     }
-    if (method.isConstructor()) {
-      String message = RefactoringBundle.getCannotRefactorMessage(RefactoringBundle.message("replace.with.method.call.does.not.work.for.constructors"));
-      showErrorMessage(message, project, editor);
-    }
-    final PsiCodeBlock body = method.getBody();
-    if (body == null) {
-      String message = RefactoringBundle.getCannotRefactorMessage(RefactoringBundle.message("method.does.not.have.a.body", method.getName()));
-      showErrorMessage(message, project, editor);
-      return;
-    }
-    final PsiStatement[] statements = body.getStatements();
-    if (statements.length == 0) {
-      String message = RefactoringBundle.getCannotRefactorMessage(RefactoringBundle.message("method.has.an.empty.body", method.getName()));
 
-      showErrorMessage(message, project, editor);
-      return;
-    }
     final AnalysisScope scope = new AnalysisScope(file);
     final Module module = ModuleUtil.findModuleForPsiElement(file);
     final BaseAnalysisActionDialog dlg = new BaseAnalysisActionDialog(RefactoringBundle.message("replace.method.duplicates.scope.chooser.title", REFACTORING_NAME),
@@ -102,31 +79,64 @@ public class MethodDuplicatesHandler implements RefactoringActionHandler {
         @Override
         public void run() {
           ProgressManager.getInstance().getProgressIndicator().setIndeterminate(true);
-          invokeOnScope(project, method, dlg.getScope(AnalysisUIOptions.getInstance(project), scope, project, module));
+          invokeOnScope(project, member, dlg.getScope(AnalysisUIOptions.getInstance(project), scope, project, module));
         }
-      }, "Locate method duplicates", true, project) ;
+      }, "Locate duplicates", true, project) ;
+    }
+  }
+
+  @Nullable
+  private static String getCannotRefactorMessage(PsiMember member) {
+    if (member == null) {
+      return RefactoringBundle.message("locate.caret.inside.a.method");
+    }
+    if (member instanceof PsiMethod) {
+      if (((PsiMethod)member).isConstructor()) {
+        return RefactoringBundle.message("replace.with.method.call.does.not.work.for.constructors");
+      }
+      final PsiCodeBlock body = ((PsiMethod)member).getBody();
+      if (body == null) {
+        return RefactoringBundle.message("method.does.not.have.a.body", member.getName());
+      }
+      final PsiStatement[] statements = body.getStatements();
+      if (statements.length == 0) {
+        return RefactoringBundle.message("method.has.an.empty.body", member.getName());
+      }
+    } else if (member instanceof PsiField) {
+      final PsiField field = (PsiField)member;
+      if (!field.hasInitializer()) {
+        return "Field " + member.getName() + " doesn't have initializer";
+      }
+      final PsiClass containingClass = field.getContainingClass();
+      if (!field.hasModifierProperty(PsiModifier.FINAL) || !field.hasModifierProperty(PsiModifier.STATIC) ||
+          containingClass == null || containingClass.getQualifiedName() == null) {
+        return "Replace Duplicates works with constants only";
+      }
+    } else {
+      return "Caret should be inside method or constant";
     }
+    return null;
   }
 
-  public static void invokeOnScope(final Project project, final PsiMethod method, final AnalysisScope scope) {
-    invokeOnScope(project, Collections.singleton(method), scope, false);
+  public static void invokeOnScope(final Project project, final PsiMember member, final AnalysisScope scope) {
+    invokeOnScope(project, Collections.singleton(member), 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>>();
+  public static void invokeOnScope(final Project project, final Set<PsiMember> members, final AnalysisScope scope, boolean silent) {
+    final Map<PsiMember, List<Match>> duplicates = new HashMap<PsiMember, 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;
-        for (PsiMethod method : methods) {
+        for (PsiMember method : members) {
           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)) {
+            for (PsiMember psiMember : members) {
+              if (PsiTreeUtil.isAncestor(psiMember, matchStart, false) ||
+                  PsiTreeUtil.isAncestor(psiMember, matchEnd, false)) {
                 iterator.remove();
                 break;
               }
@@ -143,7 +153,7 @@ public class MethodDuplicatesHandler implements RefactoringActionHandler {
         }
       }
     });
-    replaceDuplicate(project, duplicates, methods);
+    replaceDuplicate(project, duplicates, members);
     if (!silent) {
       final Runnable nothingFoundRunnable = new Runnable() {
         @Override
@@ -163,7 +173,7 @@ public class MethodDuplicatesHandler implements RefactoringActionHandler {
     }
   }
 
-  private static void replaceDuplicate(final Project project, final Map<PsiMethod, List<Match>> duplicates, final Set<PsiMethod> methods) {
+  private static void replaceDuplicate(final Project project, final Map<PsiMember, List<Match>> duplicates, final Set<PsiMember> methods) {
     LocalHistoryAction a = LocalHistory.getInstance().startAction(REFACTORING_NAME);
     try {
       final ProgressIndicator progressIndicator = ProgressManager.getInstance().getProgressIndicator();
@@ -172,8 +182,8 @@ public class MethodDuplicatesHandler implements RefactoringActionHandler {
       final Runnable replaceRunnable = new Runnable() {
         @Override
         public void run() {
-          for (final PsiMethod method : methods) {
-            final List<Match> matches = duplicates.get(method);
+          for (final PsiMember member : methods) {
+            final List<Match> matches = duplicates.get(member);
             if (matches == null) continue;
             final int duplicatesNo = matches.size();
             WindowManager.getInstance().getStatusBar(project).setInfo(getStatusMessage(duplicatesNo));
@@ -183,7 +193,10 @@ public class MethodDuplicatesHandler implements RefactoringActionHandler {
                 PostprocessReformattingAspect.getInstance(project).postponeFormattingInside(new Runnable() {
                   @Override
                   public void run() {
-                    DuplicatesImpl.invoke(project, new MethodDuplicatesMatchProvider(method, matches));
+                    final MatchProvider matchProvider =
+                      member instanceof PsiMethod ? new MethodDuplicatesMatchProvider((PsiMethod)member, matches)
+                                                  : new ConstantMatchProvider(member, project, matches);
+                    DuplicatesImpl.invoke(project, matchProvider);
                   }
                 });
               }
@@ -200,37 +213,46 @@ public class MethodDuplicatesHandler implements RefactoringActionHandler {
     }
   }
 
-  public static List<Match> hasDuplicates(final PsiFile file, final PsiMethod method) {
-    final PsiCodeBlock body = method.getBody();
-    LOG.assertTrue(body != null);
-    final PsiStatement[] statements = body.getStatements();
-    PsiElement[] pattern = statements;
+  public static List<Match> hasDuplicates(final PsiFile file, final PsiMember member) {
+    PsiElement[] pattern;
     ReturnValue matchedReturnValue = null;
-    if (statements.length != 1 || !(statements[0] instanceof PsiReturnStatement)) {
-      final PsiStatement lastStatement = statements.length > 0 ? statements[statements.length - 1] : null;
-      if (lastStatement instanceof PsiReturnStatement) {
-        final PsiExpression returnValue = ((PsiReturnStatement)lastStatement).getReturnValue();
-        if (returnValue instanceof PsiReferenceExpression) {
-          final PsiElement resolved = ((PsiReferenceExpression)returnValue).resolve();
-          if (resolved instanceof PsiVariable) {
-            pattern = new PsiElement[statements.length - 1];
-            System.arraycopy(statements, 0, pattern, 0, statements.length - 1);
-            matchedReturnValue = new VariableReturnValue((PsiVariable)resolved);
+    if (member instanceof PsiMethod) {
+      final PsiCodeBlock body = ((PsiMethod)member).getBody();
+      LOG.assertTrue(body != null);
+      final PsiStatement[] statements = body.getStatements();
+      pattern = statements;
+      matchedReturnValue = null;
+      if (statements.length != 1 || !(statements[0] instanceof PsiReturnStatement)) {
+        final PsiStatement lastStatement = statements.length > 0 ? statements[statements.length - 1] : null;
+        if (lastStatement instanceof PsiReturnStatement) {
+          final PsiExpression returnValue = ((PsiReturnStatement)lastStatement).getReturnValue();
+          if (returnValue instanceof PsiReferenceExpression) {
+            final PsiElement resolved = ((PsiReferenceExpression)returnValue).resolve();
+            if (resolved instanceof PsiVariable) {
+              pattern = new PsiElement[statements.length - 1];
+              System.arraycopy(statements, 0, pattern, 0, statements.length - 1);
+              matchedReturnValue = new VariableReturnValue((PsiVariable)resolved);
+            }
           }
         }
+      } else {
+        final PsiExpression returnValue = ((PsiReturnStatement)statements[0]).getReturnValue();
+        if (returnValue != null) {
+          pattern = new PsiElement[]{returnValue};
+        }
       }
     } else {
-      final PsiExpression returnValue = ((PsiReturnStatement)statements[0]).getReturnValue();
-      if (returnValue != null) {
-        pattern = new PsiElement[]{returnValue};
-      }
+      pattern = new PsiElement[]{((PsiField)member).getInitializer()};
     }
     if (pattern.length == 0) {
       return Collections.emptyList();
     }
+    final List<? extends PsiVariable> inputVariables = 
+      member instanceof PsiMethod ? Arrays.asList(((PsiMethod)member).getParameterList().getParameters()) : new ArrayList<PsiVariable>();
     final DuplicatesFinder duplicatesFinder =
       new DuplicatesFinder(pattern, 
-                           new InputVariables(Arrays.asList(method.getParameterList().getParameters()), method.getProject(), new LocalSearchScope(pattern), false), matchedReturnValue,
+                           new InputVariables(inputVariables, member.getProject(), new LocalSearchScope(pattern), false), 
+                           matchedReturnValue,
                            new ArrayList<PsiVariable>());
 
     return duplicatesFinder.findDuplicates(file);
@@ -248,149 +270,4 @@ public class MethodDuplicatesHandler implements RefactoringActionHandler {
   public void invoke(@NotNull Project project, @NotNull PsiElement[] elements, DataContext dataContext) {
     throw new UnsupportedOperationException();
   }
-
-  private static class MethodDuplicatesMatchProvider implements MatchProvider {
-    private final PsiMethod myMethod;
-    private final List<Match> myDuplicates;
-
-    private MethodDuplicatesMatchProvider(PsiMethod method, List<Match> duplicates) {
-      myMethod = method;
-      myDuplicates = duplicates;
-    }
-
-    @Override
-    public PsiElement processMatch(Match match) throws IncorrectOperationException {
-      match.changeSignature(myMethod);
-      final PsiClass containingClass = myMethod.getContainingClass();
-      if (isEssentialStaticContextAbsent(match)) {
-        PsiUtil.setModifierProperty(myMethod, PsiModifier.STATIC, true);
-      }
-
-      final PsiElementFactory factory = JavaPsiFacade.getInstance(myMethod.getProject()).getElementFactory();
-      final boolean needQualifier = match.getInstanceExpression() != null;
-      final boolean needStaticQualifier = isExternal(match);
-      final boolean nameConflicts = nameConflicts(match);
-      @NonNls final String text = needQualifier || needStaticQualifier || nameConflicts
-                                  ?  "q." + myMethod.getName() + "()": myMethod.getName() + "()";
-      PsiMethodCallExpression methodCallExpression = (PsiMethodCallExpression)factory.createExpressionFromText(text, null);
-      methodCallExpression = (PsiMethodCallExpression)CodeStyleManager.getInstance(myMethod.getManager()).reformat(methodCallExpression);
-      final PsiParameter[] parameters = myMethod.getParameterList().getParameters();
-      for (final PsiParameter parameter : parameters) {
-        final List<PsiElement> parameterValue = match.getParameterValues(parameter);
-        if (parameterValue != null) {
-          for (PsiElement val : parameterValue) {
-            methodCallExpression.getArgumentList().add(val);
-          }
-        }
-        else {
-          methodCallExpression.getArgumentList().add(factory.createExpressionFromText(PsiTypesUtil.getDefaultValueOfType(parameter.getType()), parameter));
-        }
-      }
-      if (needQualifier || needStaticQualifier || nameConflicts) {
-        final PsiExpression qualifierExpression = methodCallExpression.getMethodExpression().getQualifierExpression();
-        LOG.assertTrue(qualifierExpression != null);
-        if (needQualifier) {
-          qualifierExpression.replace(match.getInstanceExpression());
-        } else if (needStaticQualifier || myMethod.hasModifierProperty(PsiModifier.STATIC)) {
-          qualifierExpression.replace(factory.createReferenceExpression(containingClass));
-        } else {
-          final PsiClass psiClass = PsiTreeUtil.getParentOfType(match.getMatchStart(), PsiClass.class);
-          if (psiClass != null && psiClass.isInheritor(containingClass, true)) {
-            qualifierExpression.replace(RefactoringUtil.createSuperExpression(containingClass.getManager(), null));
-          } else {
-            qualifierExpression.replace(RefactoringUtil.createThisExpression(containingClass.getManager(), containingClass));
-          }
-        }
-      }
-      VisibilityUtil.escalateVisibility(myMethod, match.getMatchStart());
-      final PsiCodeBlock body = myMethod.getBody();
-      assert body != null;
-      final PsiStatement[] statements = body.getStatements();
-      if (statements[statements.length - 1] instanceof PsiReturnStatement) {
-        final PsiExpression value = ((PsiReturnStatement)statements[statements.length - 1]).getReturnValue();
-        if (value instanceof PsiReferenceExpression) {
-          final PsiElement var = ((PsiReferenceExpression)value).resolve();
-          if (var instanceof PsiVariable) {
-            match.replace(myMethod, methodCallExpression, (PsiVariable)var);
-            return methodCallExpression;
-          }
-        }
-      }
-      return match.replace(myMethod, methodCallExpression, null);
-    }
-
-
-
-    private boolean isExternal(final Match match) {
-      final PsiElement matchStart = match.getMatchStart();
-      final PsiClass containingClass = myMethod.getContainingClass();
-      if (PsiTreeUtil.isAncestor(containingClass, matchStart, false)) {
-        return false;
-      }
-      final PsiClass psiClass = PsiTreeUtil.getParentOfType(matchStart, PsiClass.class);
-      if (psiClass != null) {
-        if (InheritanceUtil.isInheritorOrSelf(psiClass, containingClass, true)) return false;
-      }
-      return true;
-    }
-
-    private boolean nameConflicts(Match match) {
-      PsiClass matchClass = PsiTreeUtil.getParentOfType(match.getMatchStart(), PsiClass.class);
-      while (matchClass != null && matchClass != myMethod.getContainingClass()) {
-        if (matchClass.findMethodsBySignature(myMethod, false).length > 0) {
-          return true;
-        }
-        matchClass = PsiTreeUtil.getParentOfType(matchClass, PsiClass.class);
-      }
-      return false;
-    }
-
-    private boolean isEssentialStaticContextAbsent(final Match match) {
-      if (!myMethod.hasModifierProperty(PsiModifier.STATIC)) {
-        final PsiExpression instanceExpression = match.getInstanceExpression();
-        if (instanceExpression != null) return false;
-        if (isExternal(match)) return true;
-        if (PsiTreeUtil.isAncestor(myMethod.getContainingClass(), match.getMatchStart(), false) && RefactoringUtil.isInStaticContext(match.getMatchStart(), myMethod.getContainingClass())) return true;
-      }
-      return false;
-    }
-
-    @Override
-    public List<Match> getDuplicates() {
-      return myDuplicates;
-    }
-
-    @Override
-    public boolean hasDuplicates() {
-      return myDuplicates.isEmpty();
-    }
-
-    @Override
-    @Nullable
-    public String getConfirmDuplicatePrompt(final Match match) {
-      final PsiElement matchStart = match.getMatchStart();
-      String visibility = VisibilityUtil.getPossibleVisibility(myMethod, matchStart);
-      final boolean shouldBeStatic = isEssentialStaticContextAbsent(match);
-      final String signature = match.getChangedSignature(myMethod, myMethod.hasModifierProperty(PsiModifier.STATIC) || shouldBeStatic, visibility);
-      if (signature != null) {
-        return RefactoringBundle.message("replace.this.code.fragment.and.change.signature", signature);
-      }
-      final boolean needToEscalateVisibility = !PsiUtil.isAccessible(myMethod, matchStart, null);
-      if (needToEscalateVisibility) {
-        final String visibilityPresentation = VisibilityUtil.toPresentableText(visibility);
-        return shouldBeStatic
-               ? RefactoringBundle.message("replace.this.code.fragment.and.make.method.static.visible", visibilityPresentation)
-               : RefactoringBundle.message("replace.this.code.fragment.and.make.method.visible", visibilityPresentation);
-      }
-      if (shouldBeStatic) {
-        return RefactoringBundle.message("replace.this.code.fragment.and.make.method.static");
-      }
-      return null;
-    }
-
-    @Override
-    public String getReplaceDuplicatesTitle(int idx, int size) {
-      return RefactoringBundle.message("process.methods.duplicates.title", idx, size, myMethod.getName());
-    }
-  }
 }
diff --git a/java/java-impl/src/com/intellij/refactoring/util/duplicates/MethodDuplicatesMatchProvider.java b/java/java-impl/src/com/intellij/refactoring/util/duplicates/MethodDuplicatesMatchProvider.java
new file mode 100644 (file)
index 0000000..553b708
--- /dev/null
@@ -0,0 +1,182 @@
+/*
+ * Copyright 2000-2012 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.intellij.refactoring.util.duplicates;
+
+import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.psi.*;
+import com.intellij.psi.codeStyle.CodeStyleManager;
+import com.intellij.psi.util.InheritanceUtil;
+import com.intellij.psi.util.PsiTreeUtil;
+import com.intellij.psi.util.PsiTypesUtil;
+import com.intellij.psi.util.PsiUtil;
+import com.intellij.refactoring.RefactoringBundle;
+import com.intellij.refactoring.util.RefactoringUtil;
+import com.intellij.util.IncorrectOperationException;
+import com.intellij.util.VisibilityUtil;
+import org.jetbrains.annotations.NonNls;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.List;
+
+/**
+* User: anna
+* Date: 1/16/12
+*/
+class MethodDuplicatesMatchProvider implements MatchProvider {
+  private final PsiMethod myMethod;
+  private final List<Match> myDuplicates;
+  private static final Logger LOG = Logger.getInstance("#" + MethodDuplicatesMatchProvider.class.getName());
+
+  MethodDuplicatesMatchProvider(PsiMethod method, List<Match> duplicates) {
+    myMethod = method;
+    myDuplicates = duplicates;
+  }
+
+  @Override
+  public PsiElement processMatch(Match match) throws IncorrectOperationException {
+    match.changeSignature(myMethod);
+    final PsiClass containingClass = myMethod.getContainingClass();
+    if (isEssentialStaticContextAbsent(match)) {
+      PsiUtil.setModifierProperty(myMethod, PsiModifier.STATIC, true);
+    }
+
+    final PsiElementFactory factory = JavaPsiFacade.getInstance(myMethod.getProject()).getElementFactory();
+    final boolean needQualifier = match.getInstanceExpression() != null;
+    final boolean needStaticQualifier = isExternal(match);
+    final boolean nameConflicts = nameConflicts(match);
+    @NonNls final String text = needQualifier || needStaticQualifier || nameConflicts
+                                ?  "q." + myMethod.getName() + "()": myMethod.getName() + "()";
+    PsiMethodCallExpression methodCallExpression = (PsiMethodCallExpression)factory.createExpressionFromText(text, null);
+    methodCallExpression = (PsiMethodCallExpression)CodeStyleManager.getInstance(myMethod.getManager()).reformat(methodCallExpression);
+    final PsiParameter[] parameters = myMethod.getParameterList().getParameters();
+    for (final PsiParameter parameter : parameters) {
+      final List<PsiElement> parameterValue = match.getParameterValues(parameter);
+      if (parameterValue != null) {
+        for (PsiElement val : parameterValue) {
+          methodCallExpression.getArgumentList().add(val);
+        }
+      }
+      else {
+        methodCallExpression.getArgumentList().add(factory.createExpressionFromText(PsiTypesUtil.getDefaultValueOfType(parameter.getType()), parameter));
+      }
+    }
+    if (needQualifier || needStaticQualifier || nameConflicts) {
+      final PsiExpression qualifierExpression = methodCallExpression.getMethodExpression().getQualifierExpression();
+      LOG.assertTrue(qualifierExpression != null);
+      if (needQualifier) {
+        qualifierExpression.replace(match.getInstanceExpression());
+      } else if (needStaticQualifier || myMethod.hasModifierProperty(PsiModifier.STATIC)) {
+        qualifierExpression.replace(factory.createReferenceExpression(containingClass));
+      } else {
+        final PsiClass psiClass = PsiTreeUtil.getParentOfType(match.getMatchStart(), PsiClass.class);
+        if (psiClass != null && psiClass.isInheritor(containingClass, true)) {
+          qualifierExpression.replace(RefactoringUtil.createSuperExpression(containingClass.getManager(), null));
+        } else {
+          qualifierExpression.replace(RefactoringUtil.createThisExpression(containingClass.getManager(), containingClass));
+        }
+      }
+    }
+    VisibilityUtil.escalateVisibility(myMethod, match.getMatchStart());
+    final PsiCodeBlock body = myMethod.getBody();
+    assert body != null;
+    final PsiStatement[] statements = body.getStatements();
+    if (statements[statements.length - 1] instanceof PsiReturnStatement) {
+      final PsiExpression value = ((PsiReturnStatement)statements[statements.length - 1]).getReturnValue();
+      if (value instanceof PsiReferenceExpression) {
+        final PsiElement var = ((PsiReferenceExpression)value).resolve();
+        if (var instanceof PsiVariable) {
+          match.replace(myMethod, methodCallExpression, (PsiVariable)var);
+          return methodCallExpression;
+        }
+      }
+    }
+    return match.replace(myMethod, methodCallExpression, null);
+  }
+
+
+
+  private boolean isExternal(final Match match) {
+    final PsiElement matchStart = match.getMatchStart();
+    final PsiClass containingClass = myMethod.getContainingClass();
+    if (PsiTreeUtil.isAncestor(containingClass, matchStart, false)) {
+      return false;
+    }
+    final PsiClass psiClass = PsiTreeUtil.getParentOfType(matchStart, PsiClass.class);
+    if (psiClass != null) {
+      if (InheritanceUtil.isInheritorOrSelf(psiClass, containingClass, true)) return false;
+    }
+    return true;
+  }
+
+  private boolean nameConflicts(Match match) {
+    PsiClass matchClass = PsiTreeUtil.getParentOfType(match.getMatchStart(), PsiClass.class);
+    while (matchClass != null && matchClass != myMethod.getContainingClass()) {
+      if (matchClass.findMethodsBySignature(myMethod, false).length > 0) {
+        return true;
+      }
+      matchClass = PsiTreeUtil.getParentOfType(matchClass, PsiClass.class);
+    }
+    return false;
+  }
+
+  private boolean isEssentialStaticContextAbsent(final Match match) {
+    if (!myMethod.hasModifierProperty(PsiModifier.STATIC)) {
+      final PsiExpression instanceExpression = match.getInstanceExpression();
+      if (instanceExpression != null) return false;
+      if (isExternal(match)) return true;
+      if (PsiTreeUtil.isAncestor(myMethod.getContainingClass(), match.getMatchStart(), false) && RefactoringUtil.isInStaticContext(match.getMatchStart(), myMethod.getContainingClass())) return true;
+    }
+    return false;
+  }
+
+  @Override
+  public List<Match> getDuplicates() {
+    return myDuplicates;
+  }
+
+  @Override
+  public boolean hasDuplicates() {
+    return myDuplicates.isEmpty();
+  }
+
+  @Override
+  @Nullable
+  public String getConfirmDuplicatePrompt(final Match match) {
+    final PsiElement matchStart = match.getMatchStart();
+    String visibility = VisibilityUtil.getPossibleVisibility(myMethod, matchStart);
+    final boolean shouldBeStatic = isEssentialStaticContextAbsent(match);
+    final String signature = match.getChangedSignature(myMethod, myMethod.hasModifierProperty(PsiModifier.STATIC) || shouldBeStatic, visibility);
+    if (signature != null) {
+      return RefactoringBundle.message("replace.this.code.fragment.and.change.signature", signature);
+    }
+    final boolean needToEscalateVisibility = !PsiUtil.isAccessible(myMethod, matchStart, null);
+    if (needToEscalateVisibility) {
+      final String visibilityPresentation = VisibilityUtil.toPresentableText(visibility);
+      return shouldBeStatic
+             ? RefactoringBundle.message("replace.this.code.fragment.and.make.method.static.visible", visibilityPresentation)
+             : RefactoringBundle.message("replace.this.code.fragment.and.make.method.visible", visibilityPresentation);
+    }
+    if (shouldBeStatic) {
+      return RefactoringBundle.message("replace.this.code.fragment.and.make.method.static");
+    }
+    return null;
+  }
+
+  @Override
+  public String getReplaceDuplicatesTitle(int idx, int size) {
+    return RefactoringBundle.message("process.methods.duplicates.title", idx, size, myMethod.getName());
+  }
+}
diff --git a/java/java-tests/testData/refactoring/methodDuplicatesMisc/AnonymousInitializer.java b/java/java-tests/testData/refactoring/methodDuplicatesMisc/AnonymousInitializer.java
new file mode 100644 (file)
index 0000000..d331f3a
--- /dev/null
@@ -0,0 +1,15 @@
+public class Model {
+  public static final Runnable FO<caret>O = new Runnable() {
+    public void run() {
+      System.out.println("abc");
+    }
+  };
+
+  public void foo() {
+    new Runnable() {
+      public void run() {
+        System.out.println("abc");
+      }
+    }.run();
+  }
+}
diff --git a/java/java-tests/testData/refactoring/methodDuplicatesMisc/AnonymousInitializer.java.after b/java/java-tests/testData/refactoring/methodDuplicatesMisc/AnonymousInitializer.java.after
new file mode 100644 (file)
index 0000000..a52116c
--- /dev/null
@@ -0,0 +1,11 @@
+public class Model {
+  public static final Runnable FOO = new Runnable() {
+    public void run() {
+      System.out.println("abc");
+    }
+  };
+
+  public void foo() {
+    FOO.run();
+  }
+}
diff --git a/java/java-tests/testData/refactoring/methodDuplicatesMisc/SimpleConstant.java b/java/java-tests/testData/refactoring/methodDuplicatesMisc/SimpleConstant.java
new file mode 100644 (file)
index 0000000..4f7de36
--- /dev/null
@@ -0,0 +1,12 @@
+class Test {
+    public static final String A<caret>BC = "abc";
+    void foo() {
+        System.out.println("abc");
+    }
+}
+
+class Foo {
+    void bar() {
+        System.out.println("abc");
+    }
+}
\ No newline at end of file
diff --git a/java/java-tests/testData/refactoring/methodDuplicatesMisc/SimpleConstant.java.after b/java/java-tests/testData/refactoring/methodDuplicatesMisc/SimpleConstant.java.after
new file mode 100644 (file)
index 0000000..c15a790
--- /dev/null
@@ -0,0 +1,12 @@
+class Test {
+    public static final String ABC = "abc";
+    void foo() {
+        System.out.println(ABC);
+    }
+}
+
+class Foo {
+    void bar() {
+        System.out.println(Test.ABC);
+    }
+}
\ No newline at end of file
index fb2995aa32b50107a7257f832d932ca1fad3daad..fb3051160cf4c1a4f347a4938b6d5b8d94b97808 100644 (file)
@@ -8,7 +8,7 @@ import com.intellij.JavaTestUtil;
 import com.intellij.analysis.AnalysisScope;
 import com.intellij.codeInsight.TargetElementUtilBase;
 import com.intellij.psi.PsiElement;
-import com.intellij.psi.PsiMethod;
+import com.intellij.psi.PsiMember;
 import com.intellij.refactoring.util.duplicates.MethodDuplicatesHandler;
 import com.intellij.testFramework.LightCodeInsightTestCase;
 import com.intellij.util.ui.UIUtil;
@@ -27,8 +27,8 @@ public abstract class FindMethodDuplicatesBaseTest extends LightCodeInsightTestC
     final String filePath = getTestFilePath();
     configureByFile(filePath);
     final PsiElement targetElement = TargetElementUtilBase.findTargetElement(getEditor(), TargetElementUtilBase.ELEMENT_NAME_ACCEPTED);
-    assertTrue("<caret> is not on method name", targetElement instanceof PsiMethod);
-    final PsiMethod psiMethod = (PsiMethod)targetElement;
+    assertTrue("<caret> is not on method name", targetElement instanceof PsiMember);
+    final PsiMember psiMethod = (PsiMember)targetElement;
 
     try {
       MethodDuplicatesHandler.invokeOnScope(getProject(), psiMethod, new AnalysisScope(getFile()));
index fa0fbcbb3f569e5e043780e2eb4d8d3246b716d6..914a4d4c81b8a97f2996800fb27180b155d512bf 100644 (file)
@@ -93,4 +93,12 @@ public class FindMethodDuplicatesMiscTest extends FindMethodDuplicatesBaseTest {
   public void testQualifiers() throws Exception {
     doTest();
   }
+
+  public void testSimpleConstant() throws Exception {
+    doTest();
+  }
+
+  public void testAnonymousInitializer() throws Exception {
+    doTest();
+  }
 }
\ No newline at end of file
index 7708ba1d94b49fb0dce7b8a466cdb36883374f5a..0ce17595a1ab51175dd1c95efea66c021a908141 100644 (file)
@@ -521,8 +521,8 @@ action.ExtractMethod.text=_Method...
 action.ExtractMethod.description=Turn the selected code fragment into a method
 action.RemoveMiddleman.text=Remove _Middleman...
 action.RemoveMiddleman.description=Get the client to call the delegate directly
-action.MethodDuplicates.text=Replace Met_hod Code Duplicates...
-action.MethodDuplicates.description=Finds code in current file that can be transformed into a call of selected method
+action.MethodDuplicates.text=Find and Replace _Code Duplicates...
+action.MethodDuplicates.description=Finds code in selected scope that can be transformed into a call of selected method/constant
 action.InvertBoolean.text=Invert _Boolean...
 action.InvertBoolean.description=Makes the method return or variable contain the opposite value and corrects the references
 action.IntroduceParameterObject.text=Parameter Ob_ject...
index 7c8e3d72a05c482490264e75b72e6a7b42aa7556..57802e14b96edf8e7d64c91ffff182a1b4f835f5 100644 (file)
@@ -247,12 +247,12 @@ variable.is.accessed.for.writing=Variable ''{0}'' is accessed for writing.
 variable.is.accessed.for.writing.and.used.with.inlined=Another variable ''{0}'' definition is used together with inlined one.
 only.fields.variables.of.methods.of.valid.type.can.be.considered=Only fields, variables, method parameters\u00A0or methods of valid type can be considered.
 unable.to.start.type.migration=Unable to start type migration
-replace.method.code.duplicates.title=Replace Method Code Duplicates
-locate.caret.inside.a.method=Locate caret inside a method.
+replace.method.code.duplicates.title=Replace Code Duplicates
+locate.caret.inside.a.method=Locate caret inside a member.
 replace.with.method.call.does.not.work.for.constructors=Replace With Method Call does not work for constructors
 method.does.not.have.a.body=Method {0} does not have a body.
 method.has.an.empty.body=Method {0} has an empty body.
-idea.has.not.found.any.code.that.can.be.replaced.with.method.call={0} has not found any code that can be replaced with method call
+idea.has.not.found.any.code.that.can.be.replaced.with.method.call={0} has not found any duplicates
 method.duplicates.found.message={0, choice, 1#1 code fragment|2#{0,number} code fragments} found
 0.with.1.visibility.is.not.accessible.from.2={0} with {1} visibility won''t be accessible from {2}
 0.contains.call.with.null.argument.for.parameter.1={0} contains call with null argument for parameter {1}