deep make static

This commit is contained in:
Anna Kozlova
2014-11-21 11:24:02 +01:00
parent 7b46e0b3e3
commit 90c76da057
10 changed files with 265 additions and 10 deletions
@@ -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);
}
}
@@ -131,7 +131,7 @@ public class MakeClassStaticProcessor extends MakeMethodOrClassStaticProcessor<P
}
setupTypeParameterList();
setupTypeParameterList(myMember);
// Add static modifier
final PsiModifierList modifierList = myMember.getModifierList();
@@ -111,16 +111,21 @@ public abstract class MakeMethodOrClassStaticProcessor<T extends PsiTypeParamete
refUsages.set(filterInternalUsages(usagesIn));
}
}
refUsages.set(filterOverriding(usagesIn));
final ArrayList<UsageInfo> 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<UsageInfo> toMakeStatic) {return true;}
private static UsageInfo[] filterOverriding(UsageInfo[] usages, List<UsageInfo> suggestToMakeStatic) {
ArrayList<UsageInfo> result = new ArrayList<UsageInfo>();
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<T extends PsiTypeParamete
if (!PsiTreeUtil.isAncestor(myMember, element, true) || qualifier != null) {
result.add(new UsageInfo(element));
}
processExternalReference(element, method, result);
}
}
protected void processExternalReference(PsiElement element, PsiMethod method, ArrayList<UsageInfo> 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);
}
@@ -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<PsiMethod> {
private static final Logger LOG = Logger.getInstance("#com.intellij.refactoring.makeMethodStatic.MakeMethodStaticProcessor");
private List<PsiMethod> myAdditionalMethods;
public MakeMethodStaticProcessor(final Project project, final PsiMethod method, final Settings settings) {
super(project, method, settings);
}
@Override
protected boolean findAdditionalMembers(ArrayList<UsageInfo> toMakeStatic) {
if (!toMakeStatic.isEmpty()) {
myAdditionalMethods = new ArrayList<PsiMethod>();
if (ApplicationManager.getApplication().isUnitTestMode()) {
for (UsageInfo usageInfo : toMakeStatic) {
myAdditionalMethods.add((PsiMethod)usageInfo.getElement());
}
}
else {
final JavaCallerChooser chooser = new MakeStaticJavaCallerChooser(myMember, myProject, new Consumer<Set<PsiMethod>>() {
@Override
public void consume(Set<PsiMethod> methods) {
myAdditionalMethods.addAll(methods);
}
});
if (!chooser.showAndGet()) {
return false;
}
}
}
return true;
}
@Override
protected MultiMap<PsiElement, String> getConflictDescriptions(UsageInfo[] usages) {
MultiMap<PsiElement, String> 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<UsageInfo> result) {
findExternalReferences(myMember, result);
}
@Override
protected void processExternalReference(PsiElement element, PsiMethod method, ArrayList<UsageInfo> result) {
if (!mySettings.isChangeSignature()) {
final PsiMethod containingMethod = MakeStaticJavaCallerChooser.isTheLastClassRef(element, method);
if (containingMethod != null) {
result.add(new ChainedCallUsageInfo(containingMethod));
}
}
}
}
@@ -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<Set<PsiMethod>> 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<PsiMethod> 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<PsiMethod> called,
Runnable cancelCallback,
Project project) {
super(currentMethod, called, project, cancelCallback);
myCurrentMethod = currentMethod;
}
@Override
protected MethodNodeBase<PsiMethod> createNode(PsiMethod caller, HashSet<PsiMethod> called) {
return new MakeStaticJavaMethodNode(caller, called, myCancelCallback, myProject);
}
@Override
protected Condition<PsiMethod> getFilter() {
return new Condition<PsiMethod>() {
@Override
public boolean value(PsiMethod method) {
return !myCurrentMethod.equals(method) && isTheLastClassRef(method, myCurrentMethod) != null;
}
};
}
}
}
@@ -0,0 +1,11 @@
class P {
static void foo() {
foo();
bazz(0);
}
private static void bazz(int k) {
bazz(k);
bazz(k);
}
}
@@ -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);
}
}
@@ -0,0 +1,11 @@
class P {
void foo() {
foo();
bazz(0);
}
private void <caret>bazz(int k) {
bazz(k);
bazz(k);
}
}
@@ -0,0 +1,15 @@
class PP {
void foo(){}
}
class PPImpl extends PP {
void foo() {
foo();
bazz(0);
}
private void <caret>bazz(int k) {
bazz(k);
bazz(k);
}
}
@@ -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();