From 90c76da05798926cca9512c232a7ff8cb8605860 Mon Sep 17 00:00:00 2001 From: Anna Kozlova Date: Thu, 20 Nov 2014 20:47:50 +0100 Subject: [PATCH] deep make static --- .../makeStatic/ChainedCallUsageInfo.java | 34 +++++++ .../makeStatic/MakeClassStaticProcessor.java | 2 +- .../MakeMethodOrClassStaticProcessor.java | 23 +++-- .../makeStatic/MakeMethodStaticProcessor.java | 53 +++++++++- .../MakeStaticJavaCallerChooser.java | 99 +++++++++++++++++++ .../makeMethodStatic/afterDeep.java | 11 +++ .../makeMethodStatic/afterOverrides.java | 15 +++ .../makeMethodStatic/beforeDeep.java | 11 +++ .../makeMethodStatic/beforeOverrides.java | 15 +++ .../refactoring/MakeMethodStaticTest.java | 12 +++ 10 files changed, 265 insertions(+), 10 deletions(-) create mode 100644 java/java-impl/src/com/intellij/refactoring/makeStatic/ChainedCallUsageInfo.java create mode 100644 java/java-impl/src/com/intellij/refactoring/makeStatic/MakeStaticJavaCallerChooser.java create mode 100644 java/java-tests/testData/refactoring/makeMethodStatic/afterDeep.java create mode 100644 java/java-tests/testData/refactoring/makeMethodStatic/afterOverrides.java create mode 100644 java/java-tests/testData/refactoring/makeMethodStatic/beforeDeep.java create mode 100644 java/java-tests/testData/refactoring/makeMethodStatic/beforeOverrides.java 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();