diff --git a/java/java-impl/src/com/intellij/refactoring/makeStatic/ChainedCallUsageInfo.java b/java/java-impl/src/com/intellij/refactoring/makeStatic/ChainedCallUsageInfo.java
new file mode 100644
index 000000000000..59386e4d6789
--- /dev/null
+++ b/java/java-impl/src/com/intellij/refactoring/makeStatic/ChainedCallUsageInfo.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2000-2014 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.
+ */
+
+/*
+ * Created by IntelliJ IDEA.
+ * User: dsl
+ * Date: 16.04.2002
+ * Time: 17:09:40
+ * To change template for new class use
+ * Code Style | Class Templates options (Tools | IDE Options).
+ */
+package com.intellij.refactoring.makeStatic;
+
+import com.intellij.psi.PsiElement;
+import com.intellij.usageView.UsageInfo;
+
+class ChainedCallUsageInfo extends UsageInfo {
+ ChainedCallUsageInfo(PsiElement element) {
+ super(element);
+ }
+}
diff --git a/java/java-impl/src/com/intellij/refactoring/makeStatic/MakeClassStaticProcessor.java b/java/java-impl/src/com/intellij/refactoring/makeStatic/MakeClassStaticProcessor.java
index 5672203e3c17..7ed977e4e176 100644
--- a/java/java-impl/src/com/intellij/refactoring/makeStatic/MakeClassStaticProcessor.java
+++ b/java/java-impl/src/com/intellij/refactoring/makeStatic/MakeClassStaticProcessor.java
@@ -131,7 +131,7 @@ public class MakeClassStaticProcessor extends MakeMethodOrClassStaticProcessor
toMakeStatic = new ArrayList<>();
+ refUsages.set(filterOverriding(usagesIn, toMakeStatic));
+ if (!findAdditionalMembers(toMakeStatic)) return false;
prepareSuccessful();
return true;
}
- private static UsageInfo[] filterOverriding(UsageInfo[] usages) {
+ protected boolean findAdditionalMembers(ArrayList toMakeStatic) {return true;}
+
+ private static UsageInfo[] filterOverriding(UsageInfo[] usages, List suggestToMakeStatic) {
ArrayList result = new ArrayList();
for (UsageInfo usage : usages) {
- if (!(usage instanceof OverridingMethodUsageInfo)) {
+ if (usage instanceof ChainedCallUsageInfo) {
+ suggestToMakeStatic.add(usage);
+ } else if (!(usage instanceof OverridingMethodUsageInfo)) {
result.add(usage);
}
}
@@ -253,14 +258,18 @@ public abstract class MakeMethodOrClassStaticProcessor result) {}
+
//should be called before setting static modifier
- protected void setupTypeParameterList() throws IncorrectOperationException {
- final PsiTypeParameterList list = myMember.getTypeParameterList();
+ protected void setupTypeParameterList(T member) throws IncorrectOperationException {
+ final PsiTypeParameterList list = member.getTypeParameterList();
assert list != null;
- final PsiTypeParameterList newList = RefactoringUtil.createTypeParameterListWithUsedTypeParameters(myMember);
+ final PsiTypeParameterList newList = RefactoringUtil.createTypeParameterListWithUsedTypeParameters(member);
if (newList != null) {
list.replace(newList);
}
diff --git a/java/java-impl/src/com/intellij/refactoring/makeStatic/MakeMethodStaticProcessor.java b/java/java-impl/src/com/intellij/refactoring/makeStatic/MakeMethodStaticProcessor.java
index 64afbfa17e84..95c58c7cd612 100644
--- a/java/java-impl/src/com/intellij/refactoring/makeStatic/MakeMethodStaticProcessor.java
+++ b/java/java-impl/src/com/intellij/refactoring/makeStatic/MakeMethodStaticProcessor.java
@@ -15,6 +15,7 @@
*/
package com.intellij.refactoring.makeStatic;
+import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.project.Project;
import com.intellij.psi.*;
@@ -23,25 +24,53 @@ import com.intellij.psi.javadoc.PsiDocTag;
import com.intellij.psi.util.InheritanceUtil;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.psi.util.PsiUtil;
+import com.intellij.refactoring.changeSignature.inCallers.JavaCallerChooser;
import com.intellij.refactoring.util.RefactoringUtil;
import com.intellij.refactoring.util.javadoc.MethodJavaDocHelper;
import com.intellij.usageView.UsageInfo;
+import com.intellij.util.Consumer;
import com.intellij.util.IncorrectOperationException;
import com.intellij.util.containers.MultiMap;
import java.util.ArrayList;
import java.util.List;
+import java.util.Set;
/**
* @author dsl
*/
public class MakeMethodStaticProcessor extends MakeMethodOrClassStaticProcessor {
private static final Logger LOG = Logger.getInstance("#com.intellij.refactoring.makeMethodStatic.MakeMethodStaticProcessor");
+ private List myAdditionalMethods;
public MakeMethodStaticProcessor(final Project project, final PsiMethod method, final Settings settings) {
super(project, method, settings);
}
+ @Override
+ protected boolean findAdditionalMembers(ArrayList toMakeStatic) {
+ if (!toMakeStatic.isEmpty()) {
+ myAdditionalMethods = new ArrayList();
+ if (ApplicationManager.getApplication().isUnitTestMode()) {
+ for (UsageInfo usageInfo : toMakeStatic) {
+ myAdditionalMethods.add((PsiMethod)usageInfo.getElement());
+ }
+ }
+ else {
+ final JavaCallerChooser chooser = new MakeStaticJavaCallerChooser(myMember, myProject, new Consumer>() {
+ @Override
+ public void consume(Set methods) {
+ myAdditionalMethods.addAll(methods);
+ }
+ });
+ if (!chooser.showAndGet()) {
+ return false;
+ }
+ }
+ }
+ return true;
+ }
+
@Override
protected MultiMap getConflictDescriptions(UsageInfo[] usages) {
MultiMap descriptions = super.getConflictDescriptions(usages);
@@ -122,9 +151,19 @@ public class MakeMethodStaticProcessor extends MakeMethodOrClassStaticProcessor<
anchor = javaDocHelper.addParameterAfter(fieldParameter.name, anchor);
}
}
- setupTypeParameterList();
+ makeStatic(myMember);
+
+ if (myAdditionalMethods != null) {
+ for (PsiMethod method : myAdditionalMethods) {
+ makeStatic(method);
+ }
+ }
+ }
+
+ private void makeStatic(PsiMethod member) {
+ setupTypeParameterList(member);
// Add static modifier
- final PsiModifierList modifierList = myMember.getModifierList();
+ final PsiModifierList modifierList = member.getModifierList();
modifierList.setModifierProperty(PsiModifier.STATIC, true);
modifierList.setModifierProperty(PsiModifier.FINAL, false);
modifierList.setModifierProperty(PsiModifier.DEFAULT, false);
@@ -265,4 +304,14 @@ public class MakeMethodStaticProcessor extends MakeMethodOrClassStaticProcessor<
protected void findExternalUsages(final ArrayList result) {
findExternalReferences(myMember, result);
}
+
+ @Override
+ protected void processExternalReference(PsiElement element, PsiMethod method, ArrayList result) {
+ if (!mySettings.isChangeSignature()) {
+ final PsiMethod containingMethod = MakeStaticJavaCallerChooser.isTheLastClassRef(element, method);
+ if (containingMethod != null) {
+ result.add(new ChainedCallUsageInfo(containingMethod));
+ }
+ }
+ }
}
diff --git a/java/java-impl/src/com/intellij/refactoring/makeStatic/MakeStaticJavaCallerChooser.java b/java/java-impl/src/com/intellij/refactoring/makeStatic/MakeStaticJavaCallerChooser.java
new file mode 100644
index 000000000000..1cb2ae9cc5cc
--- /dev/null
+++ b/java/java-impl/src/com/intellij/refactoring/makeStatic/MakeStaticJavaCallerChooser.java
@@ -0,0 +1,99 @@
+/*
+ * Copyright 2000-2014 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.makeStatic;
+
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.util.Condition;
+import com.intellij.psi.PsiClass;
+import com.intellij.psi.PsiElement;
+import com.intellij.psi.PsiMethod;
+import com.intellij.psi.PsiModifier;
+import com.intellij.psi.util.PsiTreeUtil;
+import com.intellij.refactoring.changeSignature.MethodNodeBase;
+import com.intellij.refactoring.changeSignature.inCallers.JavaCallerChooser;
+import com.intellij.refactoring.changeSignature.inCallers.JavaMethodNode;
+import com.intellij.util.Consumer;
+
+import java.util.HashSet;
+import java.util.Set;
+
+class MakeStaticJavaCallerChooser extends JavaCallerChooser {
+ private final Project myProject;
+
+ public MakeStaticJavaCallerChooser(PsiMethod method, Project project, Consumer> consumer) {
+ super(method, project, "Select Methods To Propagate Static", null, consumer);
+ myProject = project;
+ }
+
+ static PsiMethod isTheLastClassRef(PsiElement element, PsiMethod member) {
+ final PsiMethod containingMethod = PsiTreeUtil.getParentOfType(element, PsiMethod.class, false);
+ if ( containingMethod != null &&
+ !containingMethod.isConstructor() &&
+ containingMethod.findDeepestSuperMethods().length == 0 &&
+ !containingMethod.equals(member)) {
+ final PsiClass containingClass = containingMethod.getContainingClass();
+ if (containingClass != null) {
+ final PsiClass gContainingClass = containingClass.getContainingClass();
+ if (gContainingClass == null || gContainingClass.hasModifierProperty(PsiModifier.STATIC)) {
+ final InternalUsageInfo[] refsInMember = MakeStaticUtil.findClassRefsInMember(containingMethod, true);
+ for (InternalUsageInfo info : refsInMember) {
+ final PsiElement referencedElement = info.getReferencedElement();
+ if (!member.equals(referencedElement) && !containingMethod.equals(referencedElement)) {
+ return null;
+ }
+ }
+ return containingMethod;
+ }
+ }
+ }
+ return null;
+ }
+
+ @Override
+ protected JavaMethodNode createTreeNode(PsiMethod nodeMethod,
+ com.intellij.util.containers.HashSet called,
+ Runnable cancelCallback) {
+ return new MakeStaticJavaMethodNode(nodeMethod, called, cancelCallback, nodeMethod != null ? nodeMethod.getProject() : myProject);
+ }
+
+ private static class MakeStaticJavaMethodNode extends JavaMethodNode {
+
+ private final PsiMethod myCurrentMethod;
+
+ public MakeStaticJavaMethodNode(PsiMethod currentMethod,
+ HashSet called,
+ Runnable cancelCallback,
+ Project project) {
+ super(currentMethod, called, project, cancelCallback);
+ myCurrentMethod = currentMethod;
+ }
+
+ @Override
+ protected MethodNodeBase createNode(PsiMethod caller, HashSet called) {
+ return new MakeStaticJavaMethodNode(caller, called, myCancelCallback, myProject);
+ }
+
+ @Override
+ protected Condition getFilter() {
+ return new Condition() {
+ @Override
+ public boolean value(PsiMethod method) {
+ return !myCurrentMethod.equals(method) && isTheLastClassRef(method, myCurrentMethod) != null;
+ }
+ };
+ }
+ }
+}
diff --git a/java/java-tests/testData/refactoring/makeMethodStatic/afterDeep.java b/java/java-tests/testData/refactoring/makeMethodStatic/afterDeep.java
new file mode 100644
index 000000000000..40d65f21d627
--- /dev/null
+++ b/java/java-tests/testData/refactoring/makeMethodStatic/afterDeep.java
@@ -0,0 +1,11 @@
+class P {
+ static void foo() {
+ foo();
+ bazz(0);
+ }
+
+ private static void bazz(int k) {
+ bazz(k);
+ bazz(k);
+ }
+}
\ No newline at end of file
diff --git a/java/java-tests/testData/refactoring/makeMethodStatic/afterOverrides.java b/java/java-tests/testData/refactoring/makeMethodStatic/afterOverrides.java
new file mode 100644
index 000000000000..5f25968ebf70
--- /dev/null
+++ b/java/java-tests/testData/refactoring/makeMethodStatic/afterOverrides.java
@@ -0,0 +1,15 @@
+class PP {
+ void foo(){}
+}
+
+class PPImpl extends PP {
+ void foo() {
+ foo();
+ bazz(0);
+ }
+
+ private static void bazz(int k) {
+ bazz(k);
+ bazz(k);
+ }
+}
\ No newline at end of file
diff --git a/java/java-tests/testData/refactoring/makeMethodStatic/beforeDeep.java b/java/java-tests/testData/refactoring/makeMethodStatic/beforeDeep.java
new file mode 100644
index 000000000000..2a62cc60aebb
--- /dev/null
+++ b/java/java-tests/testData/refactoring/makeMethodStatic/beforeDeep.java
@@ -0,0 +1,11 @@
+class P {
+ void foo() {
+ foo();
+ bazz(0);
+ }
+
+ private void bazz(int k) {
+ bazz(k);
+ bazz(k);
+ }
+}
\ No newline at end of file
diff --git a/java/java-tests/testData/refactoring/makeMethodStatic/beforeOverrides.java b/java/java-tests/testData/refactoring/makeMethodStatic/beforeOverrides.java
new file mode 100644
index 000000000000..3140bf5a1c84
--- /dev/null
+++ b/java/java-tests/testData/refactoring/makeMethodStatic/beforeOverrides.java
@@ -0,0 +1,15 @@
+class PP {
+ void foo(){}
+}
+
+class PPImpl extends PP {
+ void foo() {
+ foo();
+ bazz(0);
+ }
+
+ private void bazz(int k) {
+ bazz(k);
+ bazz(k);
+ }
+}
\ No newline at end of file
diff --git a/java/java-tests/testSrc/com/intellij/refactoring/MakeMethodStaticTest.java b/java/java-tests/testSrc/com/intellij/refactoring/MakeMethodStaticTest.java
index 0ef851074f68..f5ae8121f45e 100644
--- a/java/java-tests/testSrc/com/intellij/refactoring/MakeMethodStaticTest.java
+++ b/java/java-tests/testSrc/com/intellij/refactoring/MakeMethodStaticTest.java
@@ -172,6 +172,18 @@ public class MakeMethodStaticTest extends LightRefactoringTestCase {
checkResultByFile("/refactoring/makeMethodStatic/after22.java");
}
+ public void testDeepStaticOverrides() throws Exception {
+ configureByFile("/refactoring/makeMethodStatic/beforeOverrides.java");
+ perform(false);
+ checkResultByFile("/refactoring/makeMethodStatic/afterOverrides.java");
+ }
+
+ public void testDeepStatic() throws Exception {
+ configureByFile("/refactoring/makeMethodStatic/beforeDeep.java");
+ perform(false);
+ checkResultByFile("/refactoring/makeMethodStatic/afterDeep.java");
+ }
+
public void testPreserveTypeParams() throws Exception {
configureByFile("/refactoring/makeMethodStatic/beforePreserveTypeParams.java");
performWithFields();