java safe delete: propagate callees search across fields and field initializers

This commit is contained in:
Dmitry Batkovich
2017-08-14 16:28:34 +03:00
parent b0ba833ece
commit fe47945895
17 changed files with 503 additions and 311 deletions
@@ -0,0 +1,46 @@
/*
* Copyright 2000-2017 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.changeSignature.inCallers;
import com.intellij.openapi.fileTypes.StdFileTypes;
import com.intellij.openapi.project.Project;
import com.intellij.psi.PsiMember;
import com.intellij.refactoring.changeSignature.CallerChooserBase;
import com.intellij.ui.treeStructure.Tree;
import com.intellij.util.Consumer;
import org.jetbrains.annotations.NotNull;
import java.util.Set;
public abstract class AbstractJavaMemberCallerChooser<M extends PsiMember> extends CallerChooserBase<M> {
public AbstractJavaMemberCallerChooser(M member, Project project, String title, Tree previousTree, Consumer<Set<M>> callback) {
super(member, project, title, previousTree, "dummy." + StdFileTypes.JAVA.getDefaultExtension(), callback);
}
@NotNull
protected abstract String getMemberTypePresentableText();
@Override
protected String getEmptyCallerText() {
return "Caller " + getMemberTypePresentableText() + " text \nwith highlighted callee call would be shown here";
}
@Override
protected String getEmptyCalleeText() {
return "Callee " + getMemberTypePresentableText() + " text would be shown here";
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2000-2010 JetBrains s.r.o.
* Copyright 2000-2017 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.
@@ -15,24 +15,33 @@
*/
package com.intellij.refactoring.changeSignature.inCallers;
import com.intellij.openapi.fileTypes.StdFileTypes;
import com.intellij.openapi.project.Project;
import com.intellij.psi.PsiMethod;
import com.intellij.refactoring.changeSignature.CallerChooserBase;
import com.intellij.refactoring.changeSignature.MemberNodeBase;
import com.intellij.ui.treeStructure.Tree;
import com.intellij.util.Consumer;
import com.intellij.util.containers.HashSet;
import org.jetbrains.annotations.NotNull;
import java.util.Set;
public class JavaCallerChooser extends CallerChooserBase<PsiMethod> {
public class JavaCallerChooser extends AbstractJavaMemberCallerChooser<PsiMethod> {
public JavaCallerChooser(PsiMethod method,
Project project,
String title,
Tree previousTree,
Consumer<Set<PsiMethod>> callback) {
super(method, project, title, previousTree, callback);
}
public JavaCallerChooser(PsiMethod method, Project project, String title, Tree previousTree, Consumer<Set<PsiMethod>> callback) {
super(method, project, title, previousTree, "dummy." + StdFileTypes.JAVA.getDefaultExtension(), callback);
@NotNull
@Override
protected String getMemberTypePresentableText() {
return "method";
}
@Override
protected JavaMethodNode createTreeNode(PsiMethod method, HashSet<PsiMethod> called, Runnable cancelCallback) {
protected MemberNodeBase<PsiMethod> createTreeNode(PsiMethod method, HashSet<PsiMethod> called, Runnable cancelCallback) {
return new JavaMethodNode(method, called, myProject, cancelCallback);
}
@@ -40,14 +49,4 @@ public class JavaCallerChooser extends CallerChooserBase<PsiMethod> {
protected PsiMethod[] findDeepestSuperMethods(PsiMethod method) {
return method.findDeepestSuperMethods();
}
@Override
protected String getEmptyCallerText() {
return "Caller method text \nwith highlighted callee call would be shown here";
}
@Override
protected String getEmptyCalleeText() {
return "Callee method text would be shown here";
}
}
@@ -0,0 +1,76 @@
/*
* Copyright 2000-2017 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.changeSignature.inCallers;
import com.intellij.ide.hierarchy.JavaHierarchyUtil;
import com.intellij.openapi.project.Project;
import com.intellij.psi.*;
import com.intellij.psi.presentation.java.ClassPresentationUtil;
import com.intellij.psi.util.PsiFormatUtil;
import com.intellij.psi.util.PsiFormatUtilBase;
import com.intellij.refactoring.changeSignature.MemberNodeBase;
import com.intellij.ui.ColoredTreeCellRenderer;
import com.intellij.ui.JBColor;
import com.intellij.ui.SimpleTextAttributes;
import com.intellij.util.ui.UIUtil;
import org.jetbrains.annotations.NotNull;
import java.util.Set;
public abstract class JavaMemberNode<M extends PsiMember> extends MemberNodeBase<M> {
protected JavaMemberNode(final M member, Set<M> called, Project project, Runnable cancelCallback) {
super(member, called, project, cancelCallback);
}
@Override
protected void customizeRendererText(ColoredTreeCellRenderer renderer) {
final StringBuilder buffer = new StringBuilder(128);
final PsiClass containingClass = getMember().getContainingClass();
if (containingClass != null) {
buffer.append(ClassPresentationUtil.getNameForClass(containingClass, false));
buffer.append('.');
}
buffer.append(formatMember(getMember()));
final SimpleTextAttributes attributes = isEnabled() ?
new SimpleTextAttributes(SimpleTextAttributes.STYLE_PLAIN, UIUtil.getTreeForeground()) :
SimpleTextAttributes.EXCLUDED_ATTRIBUTES;
renderer.append(buffer.toString(), attributes);
if (containingClass != null) {
final String packageName = JavaHierarchyUtil.getPackageName(containingClass);
renderer.append(" (" + packageName + ")", new SimpleTextAttributes(SimpleTextAttributes.STYLE_ITALIC, JBColor.GRAY));
}
}
private static String formatMember(@NotNull PsiMember member) {
if (member instanceof PsiMethod) {
return PsiFormatUtil.formatMethod(
(PsiMethod)member,
PsiSubstitutor.EMPTY, PsiFormatUtilBase.SHOW_NAME | PsiFormatUtilBase.SHOW_PARAMETERS,
PsiFormatUtilBase.SHOW_TYPE
);
} else {
assert member instanceof PsiField;
return PsiFormatUtil.formatVariable(
(PsiField)member,
PsiFormatUtilBase.SHOW_NAME | PsiFormatUtilBase.SHOW_TYPE,
PsiSubstitutor.EMPTY
);
}
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2000-2012 JetBrains s.r.o.
* Copyright 2000-2017 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.
@@ -15,40 +15,33 @@
*/
package com.intellij.refactoring.changeSignature.inCallers;
import com.intellij.ide.hierarchy.JavaHierarchyUtil;
import com.intellij.openapi.project.Project;
import com.intellij.psi.*;
import com.intellij.psi.presentation.java.ClassPresentationUtil;
import com.intellij.psi.search.GlobalSearchScope;
import com.intellij.psi.search.searches.MethodReferencesSearch;
import com.intellij.psi.util.PsiFormatUtil;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.refactoring.changeSignature.MethodNodeBase;
import com.intellij.ui.ColoredTreeCellRenderer;
import com.intellij.ui.JBColor;
import com.intellij.ui.SimpleTextAttributes;
import com.intellij.util.ui.UIUtil;
import com.intellij.refactoring.changeSignature.MemberNodeBase;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
public class JavaMethodNode extends MethodNodeBase<PsiMethod> {
protected JavaMethodNode(final PsiMethod method, Set<PsiMethod> called, Project project, Runnable cancelCallback) {
public class JavaMethodNode extends JavaMemberNode<PsiMethod> {
protected JavaMethodNode(PsiMethod method,
Set<PsiMethod> called,
Project project,
Runnable cancelCallback) {
super(method, called, project, cancelCallback);
}
@Override
protected MethodNodeBase<PsiMethod> createNode(PsiMethod caller, HashSet<PsiMethod> called) {
protected MemberNodeBase<PsiMethod> createNode(PsiMethod caller, HashSet<PsiMethod> called) {
return new JavaMethodNode(caller, called, myProject, myCancelCallback);
}
@Override
protected List<PsiMethod> computeCallers() {
final PsiReference[] refs =
MethodReferencesSearch.search(myMethod, GlobalSearchScope.allScope(myProject), true).toArray(PsiReference.EMPTY_ARRAY);
final PsiReference[] refs = MethodReferencesSearch.search(myMethod).toArray(PsiReference.EMPTY_ARRAY);
List<PsiMethod> result = new ArrayList<>();
for (PsiReference ref : refs) {
@@ -57,7 +50,7 @@ public class JavaMethodNode extends MethodNodeBase<PsiMethod> {
!(((PsiReferenceExpression)element).getQualifierExpression() instanceof PsiSuperExpression)) {
final PsiElement enclosingContext = PsiTreeUtil.getParentOfType(element, PsiMethod.class, PsiClass.class);
if (enclosingContext instanceof PsiMethod && !result.contains(enclosingContext) &&
!myMethod.equals(enclosingContext) && !myCalled.contains(myMethod)) { //do not add recursive methods
!getMember().equals(enclosingContext) && !myCalled.contains(getMember())) { //do not add recursive methods
result.add((PsiMethod)enclosingContext);
}
else if (element instanceof PsiClass) {
@@ -71,31 +64,4 @@ public class JavaMethodNode extends MethodNodeBase<PsiMethod> {
}
return result;
}
@Override
protected void customizeRendererText(ColoredTreeCellRenderer renderer) {
final StringBuilder buffer = new StringBuilder(128);
final PsiClass containingClass = myMethod.getContainingClass();
if (containingClass != null) {
buffer.append(ClassPresentationUtil.getNameForClass(containingClass, false));
buffer.append('.');
}
final String methodText = PsiFormatUtil.formatMethod(
myMethod,
PsiSubstitutor.EMPTY, PsiFormatUtil.SHOW_NAME | PsiFormatUtil.SHOW_PARAMETERS,
PsiFormatUtil.SHOW_TYPE
);
buffer.append(methodText);
final SimpleTextAttributes attributes = isEnabled() ?
new SimpleTextAttributes(SimpleTextAttributes.STYLE_PLAIN, UIUtil.getTreeForeground()) :
SimpleTextAttributes.EXCLUDED_ATTRIBUTES;
renderer.append(buffer.toString(), attributes);
if (containingClass != null) {
final String packageName = JavaHierarchyUtil.getPackageName(containingClass);
renderer.append(" (" + packageName + ")", new SimpleTextAttributes(SimpleTextAttributes.STYLE_ITALIC, JBColor.GRAY));
}
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2000-2016 JetBrains s.r.o.
* Copyright 2000-2017 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.
@@ -24,8 +24,7 @@ import com.intellij.psi.PsiModifier;
import com.intellij.psi.search.searches.OverridingMethodsSearch;
import com.intellij.psi.util.ClassUtil;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.psi.util.PsiUtil;
import com.intellij.refactoring.changeSignature.MethodNodeBase;
import com.intellij.refactoring.changeSignature.MemberNodeBase;
import com.intellij.refactoring.changeSignature.inCallers.JavaCallerChooser;
import com.intellij.refactoring.changeSignature.inCallers.JavaMethodNode;
import com.intellij.usageView.UsageInfo;
@@ -78,7 +77,7 @@ abstract class MakeStaticJavaCallerChooser extends JavaCallerChooser {
Runnable cancelCallback) {
final MakeStaticJavaMethodNode node =
new MakeStaticJavaMethodNode(nodeMethod, called, cancelCallback, nodeMethod != null ? nodeMethod.getProject() : myProject);
if (getTopMethod().equals(nodeMethod)) {
if (getTopMember().equals(nodeMethod)) {
node.setEnabled(false);
node.setChecked(true);
}
@@ -95,7 +94,7 @@ abstract class MakeStaticJavaCallerChooser extends JavaCallerChooser {
@Override
protected List<PsiMethod> computeCallers() {
if (getTopMethod().equals(getMethod())) {
if (getTopMember().equals(getMember())) {
final ArrayList<UsageInfo> items = getTopLevelItems();
return ContainerUtil.map(items, info -> (PsiMethod)info.getElement());
}
@@ -104,7 +103,7 @@ abstract class MakeStaticJavaCallerChooser extends JavaCallerChooser {
@Override
protected MethodNodeBase<PsiMethod> createNode(PsiMethod caller, HashSet<PsiMethod> called) {
protected MemberNodeBase<PsiMethod> createNode(PsiMethod caller, HashSet<PsiMethod> called) {
return new MakeStaticJavaMethodNode(caller, called, myCancelCallback, myProject);
}
@@ -1,5 +1,5 @@
/*
* Copyright 2000-2016 JetBrains s.r.o.
* Copyright 2000-2017 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.
@@ -41,7 +41,7 @@ import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.psi.util.PsiUtil;
import com.intellij.refactoring.JavaRefactoringSettings;
import com.intellij.refactoring.RefactoringBundle;
import com.intellij.refactoring.changeSignature.inCallers.JavaCallerChooser;
import com.intellij.refactoring.changeSignature.inCallers.AbstractJavaMemberCallerChooser;
import com.intellij.refactoring.safeDelete.usageInfo.*;
import com.intellij.refactoring.util.ConflictsUtil;
import com.intellij.refactoring.util.RefactoringChangeUtil;
@@ -319,7 +319,7 @@ public class JavaSafeDeleteProcessor extends SafeDeleteProcessorDelegateBase {
final ArrayList<UsageInfo> result = new ArrayList<>();
final ArrayList<UsageInfo> overridingMethods = new ArrayList<>();
final ArrayList<SafeDeleteParameterCallHierarchyUsageInfo> delegatingParams = new ArrayList<>();
final ArrayList<SafeDeleteMethodCalleeUsageInfo> calleesSafeToDelete = new ArrayList<>();
final ArrayList<SafeDeleteMemberCalleeUsageInfo> calleesSafeToDelete = new ArrayList<>();
for (UsageInfo usage : usages) {
if (usage.isNonCodeUsage) {
if (usage instanceof SafeDeleteUsageInfo) {
@@ -335,8 +335,8 @@ public class JavaSafeDeleteProcessor extends SafeDeleteProcessorDelegateBase {
}
result.add(usage);
}
else if (usage instanceof SafeDeleteMethodCalleeUsageInfo) {
calleesSafeToDelete.add((SafeDeleteMethodCalleeUsageInfo)usage);
else if (usage instanceof SafeDeleteMemberCalleeUsageInfo) {
calleesSafeToDelete.add((SafeDeleteMemberCalleeUsageInfo)usage);
}
else if (usage instanceof SafeDeleteOverridingMethodUsageInfo) {
overridingMethods.add(usage);
@@ -389,7 +389,7 @@ public class JavaSafeDeleteProcessor extends SafeDeleteProcessorDelegateBase {
final PsiMethod method = parameterHierarchyUsageInfo.getCalledMethod();
final PsiParameter parameter = parameterHierarchyUsageInfo.getReferencedElement();
final int parameterIndex = method.getParameterList().getParameterIndex(parameter);
final JavaCallerChooser chooser = new SafeDeleteJavaCallerChooser(method, project, result) {
final AbstractJavaMemberCallerChooser chooser = new SafeDeleteJavaCallerChooser(method, project, result) {
@Override
protected ArrayList<SafeDeleteParameterCallHierarchyUsageInfo> getTopLevelItems() {
return delegatingParams;
@@ -421,11 +421,11 @@ public class JavaSafeDeleteProcessor extends SafeDeleteProcessorDelegateBase {
result.addAll(calleesSafeToDelete);
}
else {
final PsiMethod method = calleesSafeToDelete.get(0).getCallerMethod();
final PsiMember member = calleesSafeToDelete.get(0).getCallerMember();
final ArrayList<UsageInfo> list = new ArrayList<>();
JavaCallerChooser chooser = new SafeDeleteJavaCalleeChooser(method, project, list) {
AbstractJavaMemberCallerChooser chooser = new SafeDeleteJavaCalleeChooser(member, project, list) {
@Override
protected ArrayList<SafeDeleteMethodCalleeUsageInfo> getTopLevelItems() {
protected ArrayList<SafeDeleteMemberCalleeUsageInfo> getTopLevelItems() {
return calleesSafeToDelete;
}
};
@@ -438,12 +438,14 @@ public class JavaSafeDeleteProcessor extends SafeDeleteProcessorDelegateBase {
for (UsageInfo info : list) {
methodsToDelete.add(info.getElement());
}
methodsToDelete.add(method);
methodsToDelete.add(member);
final Condition<PsiElement> insideDeletedCondition = getUsageInsideDeletedFilter(methodsToDelete.toArray(new PsiElement[methodsToDelete.size()]));
for (UsageInfo info : list) {
SafeDeleteProcessor.addNonCodeUsages(info.getElement(), result, insideDeletedCondition,
JavaRefactoringSettings.getInstance().RENAME_SEARCH_FOR_TEXT_FOR_METHOD,
JavaRefactoringSettings.getInstance().RENAME_SEARCH_IN_COMMENTS_FOR_METHOD );
PsiElement psi = info.getElement();
JavaRefactoringSettings refactoringSettings = JavaRefactoringSettings.getInstance();
SafeDeleteProcessor.addNonCodeUsages(psi, result, insideDeletedCondition,
psi instanceof PsiMethod ? refactoringSettings.RENAME_SEARCH_FOR_TEXT_FOR_METHOD : refactoringSettings.RENAME_SEARCH_FOR_TEXT_FOR_FIELD,
psi instanceof PsiMethod ? refactoringSettings.RENAME_SEARCH_IN_COMMENTS_FOR_METHOD : refactoringSettings.RENAME_SEARCH_IN_COMMENTS_FOR_FIELD );
}
}
}
@@ -684,12 +686,7 @@ public class JavaSafeDeleteProcessor extends SafeDeleteProcessorDelegateBase {
}
}
final List<PsiMethod> calleesSafeToDelete = SafeDeleteJavaCalleeChooser.computeCalleesSafeToDelete(psiMethod);
if (calleesSafeToDelete != null) {
for (PsiMethod method : calleesSafeToDelete) {
usages.add(new SafeDeleteMethodCalleeUsageInfo(method, psiMethod));
}
}
appendCallees(psiMethod, usages);
return usage -> {
if(usage instanceof PsiFile) return false;
@@ -697,6 +694,15 @@ public class JavaSafeDeleteProcessor extends SafeDeleteProcessorDelegateBase {
};
}
private static void appendCallees(@NotNull PsiMember method, @NotNull List<UsageInfo> usages) {
final List<PsiMember> calleesSafeToDelete = SafeDeleteJavaCalleeChooser.computeCalleesSafeToDelete(method);
if (calleesSafeToDelete != null) {
for (PsiMember callee : calleesSafeToDelete) {
usages.add(new SafeDeleteMemberCalleeUsageInfo(callee, method));
}
}
}
private static void findFunctionalExpressions(final List<UsageInfo> usages, PsiMethod... methods) {
for (PsiMethod method : methods) {
final PsiClass containingClass = method.getContainingClass();
@@ -943,6 +949,8 @@ public class JavaSafeDeleteProcessor extends SafeDeleteProcessorDelegateBase {
usages.add(createParameterCallHierarchyUsageInfo(setterPrototype, setterParameter, (PsiMethod)scope, parameter));
}
}
appendCallees(psiField, usages);
return isInsideDeleted;
}
@@ -1,5 +1,5 @@
/*
* Copyright 2000-2014 JetBrains s.r.o.
* Copyright 2000-2017 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.
@@ -20,67 +20,86 @@ import com.intellij.openapi.util.Condition;
import com.intellij.psi.*;
import com.intellij.psi.search.searches.ReferencesSearch;
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.refactoring.safeDelete.usageInfo.SafeDeleteMethodCalleeUsageInfo;
import com.intellij.refactoring.changeSignature.MemberNodeBase;
import com.intellij.refactoring.changeSignature.inCallers.AbstractJavaMemberCallerChooser;
import com.intellij.refactoring.changeSignature.inCallers.JavaMemberNode;
import com.intellij.refactoring.safeDelete.usageInfo.SafeDeleteMemberCalleeUsageInfo;
import com.intellij.refactoring.safeDelete.usageInfo.SafeDeleteReferenceJavaDeleteUsageInfo;
import com.intellij.usageView.UsageInfo;
import com.intellij.util.CommonProcessors;
import com.intellij.util.containers.ContainerUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.*;
import java.util.stream.Collectors;
abstract class SafeDeleteJavaCalleeChooser extends JavaCallerChooser {
abstract class SafeDeleteJavaCalleeChooser extends AbstractJavaMemberCallerChooser<PsiMember> {
private final Project myProject;
public SafeDeleteJavaCalleeChooser(final PsiMethod method, Project project, final ArrayList<UsageInfo> result) {
super(method, project, "Select Methods To Cascade Safe Delete", null, methods -> result.addAll(ContainerUtil.map(methods, m -> {
public SafeDeleteJavaCalleeChooser(PsiMember member,
Project project,
ArrayList<UsageInfo> result) {
super(member, project, "Select Members To Cascade Safe Delete", null, members -> result.addAll(ContainerUtil.map(members, m -> {
return new SafeDeleteReferenceJavaDeleteUsageInfo(m, m, true);
})));
myProject = project;
}
protected abstract ArrayList<SafeDeleteMemberCalleeUsageInfo> getTopLevelItems();
@NotNull
@Override
protected String getMemberTypePresentableText() {
return "member";
}
@Override
protected PsiMember[] findDeepestSuperMethods(PsiMember method) {
return method instanceof PsiMethod ? ((PsiMethod)method).findDeepestSuperMethods() : PsiMember.EMPTY_ARRAY;
}
@Nullable
static List<PsiMethod> computeCalleesSafeToDelete(final PsiMethod psiMethod) {
final PsiCodeBlock body = psiMethod.getBody();
static List<PsiMember> computeCalleesSafeToDelete(final PsiMember psiMember) {
final PsiElement body;
if (psiMember instanceof PsiMethod) {
body = ((PsiMethod)psiMember).getBody();
} else {
assert psiMember instanceof PsiField;
body = ((PsiField)psiMember).getInitializer();
}
if (body != null) {
final PsiClass containingClass = psiMethod.getContainingClass();
final PsiClass containingClass = psiMember.getContainingClass();
if (containingClass != null) {
final Set<PsiMethod> methodsToCheck = new HashSet<>();
final Set<PsiMember> membersToCheck = new HashSet<>();
body.accept(new JavaRecursiveElementWalkingVisitor() {
@Override
public void visitMethodCallExpression(PsiMethodCallExpression expression) {
super.visitMethodCallExpression(expression);
ContainerUtil.addAllNotNull(methodsToCheck, expression.resolveMethod());
public void visitReferenceExpression(PsiReferenceExpression expression) {
super.visitReferenceExpression(expression);
PsiElement resolved = expression.resolve();
if (resolved instanceof PsiMethod || resolved instanceof PsiField) {
ContainerUtil.addAllNotNull(membersToCheck, (PsiMember) resolved);
}
}
});
return ContainerUtil.filter(methodsToCheck, m -> containingClass.equals(m.getContainingClass()) &&
!psiMethod.equals(m) &&
m.findDeepestSuperMethods().length == 0 &&
ReferencesSearch.search(m).forEach(new CommonProcessors.CollectProcessor<PsiReference>() {
@Override
public boolean process(PsiReference reference) {
final PsiElement element = reference.getElement();
return PsiTreeUtil.isAncestor(psiMethod, element, true) ||
PsiTreeUtil.isAncestor(m, element, true);
}
}));
return membersToCheck
.stream()
.filter(m -> containingClass.equals(m.getContainingClass()) && !psiMember.equals(m))
.filter(m -> !(m instanceof PsiMethod) || ((PsiMethod)m).findDeepestSuperMethods().length == 0)
.filter(m -> usedOnlyIn(m, psiMember))
.collect(Collectors.toList());
}
}
return null;
}
protected abstract ArrayList<SafeDeleteMethodCalleeUsageInfo> getTopLevelItems();
@Override
protected JavaMethodNode createTreeNode(PsiMethod nodeMethod,
com.intellij.util.containers.HashSet<PsiMethod> callees,
protected JavaMemberNode<PsiMember> createTreeNode(PsiMember nodeMethod,
com.intellij.util.containers.HashSet<PsiMember> callees,
Runnable cancelCallback) {
final SafeDeleteJavaMethodNode node = new SafeDeleteJavaMethodNode(nodeMethod, callees, cancelCallback, nodeMethod != null ? nodeMethod.getProject() : myProject);
if (getTopMethod().equals(nodeMethod)) {
final SafeDeleteJavaMemberNode node = new SafeDeleteJavaMemberNode(nodeMethod, callees, cancelCallback, nodeMethod != null ? nodeMethod.getProject() : myProject);
if (getTopMember().equals(nodeMethod)) {
node.setEnabled(false);
node.setChecked(true);
}
@@ -88,39 +107,38 @@ abstract class SafeDeleteJavaCalleeChooser extends JavaCallerChooser {
}
@Override
protected MethodNodeBase<PsiMethod> getCalleeNode(MethodNodeBase<PsiMethod> node) {
protected MemberNodeBase<PsiMember> getCalleeNode(MemberNodeBase<PsiMember> node) {
return node;
}
@Override
protected MethodNodeBase<PsiMethod> getCallerNode(MethodNodeBase<PsiMethod> node) {
return (MethodNodeBase<PsiMethod>)node.getParent();
protected MemberNodeBase<PsiMember> getCallerNode(MemberNodeBase<PsiMember> node) {
return (MemberNodeBase<PsiMember>)node.getParent();
}
private class SafeDeleteJavaMethodNode extends JavaMethodNode {
private class SafeDeleteJavaMemberNode extends JavaMemberNode<PsiMember> {
public SafeDeleteJavaMethodNode(PsiMethod currentMethod,
HashSet<PsiMethod> callees,
public SafeDeleteJavaMemberNode(PsiMember currentMember,
HashSet<PsiMember> callees,
Runnable cancelCallback,
Project project) {
super(currentMethod, callees, project, cancelCallback);
super(currentMember, callees, project, cancelCallback);
}
@Override
protected MethodNodeBase<PsiMethod> createNode(PsiMethod caller, HashSet<PsiMethod> callees) {
return new SafeDeleteJavaMethodNode(caller, callees, myCancelCallback, myProject);
protected MemberNodeBase<PsiMember> createNode(PsiMember caller, HashSet<PsiMember> callees) {
return new SafeDeleteJavaMemberNode(caller, callees, myCancelCallback, myProject);
}
@Override
protected List<PsiMethod> computeCallers() {
if (getTopMethod().equals(getMethod())) {
return ContainerUtil.map(getTopLevelItems(), info -> info.getCalledMethod());
protected List<PsiMember> computeCallers() {
if (getTopMember().equals(getMember())) {
return ContainerUtil.map(getTopLevelItems(), info -> info.getCalledMember());
}
final List<PsiMethod> callees = computeCalleesSafeToDelete(getMethod());
final List<PsiMember> callees = computeCalleesSafeToDelete(getMember());
if (callees != null) {
callees.remove(getTopMethod());
callees.remove(getTopMember());
return callees;
}
else {
@@ -129,8 +147,20 @@ abstract class SafeDeleteJavaCalleeChooser extends JavaCallerChooser {
}
@Override
protected Condition<PsiMethod> getFilter() {
return method -> !myMethod.equals(method);
protected Condition<PsiMember> getFilter() {
return member -> !getMember().equals(member);
}
}
private static boolean usedOnlyIn(@NotNull PsiMember explored, @NotNull PsiMember place) {
return ReferencesSearch.search(explored).forEach(
new CommonProcessors.CollectProcessor<PsiReference>() {
@Override
public boolean process(PsiReference reference) {
final PsiElement element = reference.getElement();
return PsiTreeUtil.isAncestor(place, element, true) ||
PsiTreeUtil.isAncestor(explored, element, true);
}
});
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2000-2016 JetBrains s.r.o.
* Copyright 2000-2017 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.
@@ -27,13 +27,13 @@ import com.intellij.psi.search.searches.OverridingMethodsSearch;
import com.intellij.psi.search.searches.ReferencesSearch;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.psi.util.PsiUtil;
import com.intellij.refactoring.changeSignature.MethodNodeBase;
import com.intellij.refactoring.changeSignature.MemberNodeBase;
import com.intellij.refactoring.changeSignature.inCallers.JavaCallerChooser;
import com.intellij.refactoring.changeSignature.inCallers.JavaMethodNode;
import com.intellij.refactoring.safeDelete.usageInfo.SafeDeleteParameterCallHierarchyUsageInfo;
import com.intellij.refactoring.safeDelete.usageInfo.SafeDeleteReferenceJavaDeleteUsageInfo;
import com.intellij.usageView.UsageInfo;
import com.intellij.util.Consumer;
import com.intellij.util.EmptyConsumer;
import com.intellij.util.Processor;
import com.intellij.util.containers.ContainerUtil;
@@ -48,7 +48,7 @@ abstract class SafeDeleteJavaCallerChooser extends JavaCallerChooser {
private final ArrayList<UsageInfo> myResult;
public SafeDeleteJavaCallerChooser(PsiMethod method, Project project, ArrayList<UsageInfo> result) {
super(method, project, "Select Methods To Propagate Parameter Deletion", null, Consumer.EMPTY_CONSUMER);
super(method, project, "Select Methods To Propagate Parameter Deletion", null, EmptyConsumer.getInstance());
myMethod = method;
myProject = project;
myResult = result;
@@ -59,11 +59,11 @@ abstract class SafeDeleteJavaCallerChooser extends JavaCallerChooser {
@Override
protected JavaMethodNode createTreeNode(PsiMethod nodeMethod,
com.intellij.util.containers.HashSet<PsiMethod> called,
Runnable cancelCallback) {
com.intellij.util.containers.HashSet<PsiMethod> called,
Runnable cancelCallback) {
final SafeDeleteJavaMethodNode node = new SafeDeleteJavaMethodNode(nodeMethod, called, cancelCallback, getParameterIdx(),
nodeMethod != null ? nodeMethod.getProject() : myProject);
if (getTopMethod().equals(nodeMethod)) {
if (getTopMember().equals(nodeMethod)) {
node.setEnabled(false);
node.setChecked(true);
}
@@ -74,10 +74,10 @@ abstract class SafeDeleteJavaCallerChooser extends JavaCallerChooser {
protected void doOKAction() {
final List<UsageInfo> foreignMethodUsages = new ArrayList<>();
final Runnable runnable = () -> {
final Set<MethodNodeBase<PsiMethod>> nodes = getSelectedNodes();
for (MethodNodeBase<PsiMethod> node : nodes) {
final Set<MemberNodeBase<PsiMethod>> nodes = getSelectedNodes();
for (MemberNodeBase<PsiMethod> node : nodes) {
final SafeDeleteJavaMethodNode methodNode = (SafeDeleteJavaMethodNode)node;
final PsiMethod nodeMethod = methodNode.getMethod();
final PsiMethod nodeMethod = methodNode.getMember();
if (nodeMethod.equals(myMethod)) continue;
final PsiParameter parameter = nodeMethod.getParameterList().getParameters()[methodNode.myParameterIdx];
foreignMethodUsages.add(new SafeDeleteParameterCallHierarchyUsageInfo(nodeMethod, parameter, nodeMethod, parameter));
@@ -232,18 +232,18 @@ abstract class SafeDeleteJavaCallerChooser extends JavaCallerChooser {
}
@Override
protected MethodNodeBase<PsiMethod> createNode(PsiMethod caller, HashSet<PsiMethod> called) {
protected MemberNodeBase<PsiMethod> createNode(PsiMethod caller, HashSet<PsiMethod> called) {
return new SafeDeleteJavaMethodNode(caller, called, myCancelCallback, getCallerParameterIndex(myMethod, myParameterIdx, caller), myProject);
}
@Override
protected List<PsiMethod> computeCallers() {
if (getTopMethod().equals(getMethod())) {
if (getTopMember().equals(getMember())) {
final ArrayList<SafeDeleteParameterCallHierarchyUsageInfo> items = getTopLevelItems();
return ContainerUtil.map(items, info -> info.getCallerMethod());
}
final List<PsiMethod> methods = super.computeCallers();
methods.remove(getTopMethod());
methods.remove(getTopMember());
return methods;
}
@@ -1,5 +1,5 @@
/*
* Copyright 2000-2014 JetBrains s.r.o.
* Copyright 2000-2017 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.
@@ -15,33 +15,32 @@
*/
package com.intellij.refactoring.safeDelete.usageInfo;
import com.intellij.psi.PsiMethod;
import com.intellij.psi.PsiParameter;
import com.intellij.psi.PsiMember;
import com.intellij.util.IncorrectOperationException;
public class SafeDeleteMethodCalleeUsageInfo extends SafeDeleteUsageInfo implements SafeDeleteCustomUsageInfo {
public class SafeDeleteMemberCalleeUsageInfo extends SafeDeleteUsageInfo implements SafeDeleteCustomUsageInfo {
private final PsiMethod myCalledMethod;
private final PsiMethod myCallerMethod;
private final PsiMember myCalledMember;
private final PsiMember myCallerMember;
public SafeDeleteMethodCalleeUsageInfo(PsiMethod calledMethod, PsiMethod callerMethod) {
super(calledMethod, calledMethod);
myCalledMethod = calledMethod;
myCallerMethod = callerMethod;
public SafeDeleteMemberCalleeUsageInfo(PsiMember calledMember, PsiMember callerMember) {
super(calledMember, calledMember);
myCalledMember = calledMember;
myCallerMember = callerMember;
}
public void performRefactoring() throws IncorrectOperationException {
final PsiMethod callee = myCalledMethod;
final PsiMember callee = myCalledMember;
if (callee != null && callee.isValid()) {
callee.delete();
}
}
public PsiMethod getCalledMethod() {
return myCalledMethod;
public PsiMember getCalledMember() {
return myCalledMember;
}
public PsiMethod getCallerMethod() {
return myCallerMethod;
public PsiMember getCallerMember() {
return myCallerMember;
}
}
@@ -0,0 +1,8 @@
class Foo {
static final String HEL<caret>LO = createHelloText();
static String createHelloText() {
return "hello";
}
}
@@ -0,0 +1,12 @@
class Foo {
static final String WORLD = "world";
static void sayHelloWo<caret>rld() {
hello();
System.out.println(WORLD);
}
static void hello() {
System.out.println("hello");
}
}
@@ -370,6 +370,14 @@ public class SafeDeleteTest extends MultiFileTestCase {
});
}
public void testDeepDeleteFieldAndInitializerMethod() throws Exception {
doSingleFileTest();
}
public void testDeleteMethodCascadeWithField() throws Exception {
doSingleFileTest();
}
private void doTest(@NonNls final String qClassName) {
doTest((rootDir, rootAfter) -> this.performAction(qClassName));
}
@@ -1,5 +1,5 @@
/*
* Copyright 2000-2010 JetBrains s.r.o.
* Copyright 2000-2017 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.
@@ -40,7 +40,6 @@ import com.intellij.ui.*;
import com.intellij.ui.treeStructure.Tree;
import com.intellij.util.Alarm;
import com.intellij.util.Consumer;
import com.intellij.util.Function;
import com.intellij.util.Query;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.containers.HashSet;
@@ -58,7 +57,7 @@ import java.util.*;
public abstract class CallerChooserBase<M extends PsiElement> extends DialogWrapper {
private final M myMethod;
private final Alarm myAlarm = new Alarm();
private MethodNodeBase<M> myRoot;
private MemberNodeBase<M> myRoot;
protected final Project myProject;
private Tree myTree;
private final Consumer<Set<M>> myCallback;
@@ -68,7 +67,7 @@ public abstract class CallerChooserBase<M extends PsiElement> extends DialogWrap
private final boolean myInitDone;
private final String myFileName;
protected abstract MethodNodeBase<M> createTreeNode(M method, HashSet<M> called, Runnable cancelCallback);
protected abstract MemberNodeBase<M> createTreeNode(M method, HashSet<M> called, Runnable cancelCallback);
protected abstract M[] findDeepestSuperMethods(M method);
@@ -105,14 +104,14 @@ public abstract class CallerChooserBase<M extends PsiElement> extends DialogWrap
}
else {
final CheckedTreeNode root = (CheckedTreeNode)myTree.getModel().getRoot();
myRoot = (MethodNodeBase)root.getFirstChild();
myRoot = (MemberNodeBase)root.getFirstChild();
}
myTreeSelectionListener = new TreeSelectionListener() {
@Override
public void valueChanged(TreeSelectionEvent e) {
final TreePath path = e.getPath();
if (path != null) {
final MethodNodeBase<M> node = (MethodNodeBase)path.getLastPathComponent();
final MemberNodeBase<M> node = (MemberNodeBase)path.getLastPathComponent();
myAlarm.cancelAllRequests();
myAlarm.addRequest(() -> updateEditorTexts(node), 300);
}
@@ -129,7 +128,7 @@ public abstract class CallerChooserBase<M extends PsiElement> extends DialogWrap
myTree.getSelectionModel().addSelectionPath(selectionPath);
}
final MethodNodeBase<M> node = (MethodNodeBase)selectionPath.getLastPathComponent();
final MemberNodeBase<M> node = (MemberNodeBase)selectionPath.getLastPathComponent();
updateEditorTexts(node);
splitter.setSecondComponent(callSitesViewer);
@@ -137,12 +136,12 @@ public abstract class CallerChooserBase<M extends PsiElement> extends DialogWrap
return result;
}
private void updateEditorTexts(final MethodNodeBase<M> node) {
final MethodNodeBase<M> parentNode = getCalleeNode(node);
final MethodNodeBase<M> callerNode = getCallerNode(node);
final String callerText = node != myRoot ? getText(callerNode.getMethod()) : getEmptyCallerText();
private void updateEditorTexts(final MemberNodeBase<M> node) {
final MemberNodeBase<M> parentNode = getCalleeNode(node);
final MemberNodeBase<M> callerNode = getCallerNode(node);
final String callerText = node != myRoot ? getText(callerNode.getMember()) : getEmptyCallerText();
final Document callerDocument = myCallerEditor.getDocument();
final String calleeText = node != myRoot ? getText(parentNode.getMethod()) : getEmptyCalleeText();
final String calleeText = node != myRoot ? getText(parentNode.getMember()) : getEmptyCalleeText();
final Document calleeDocument = myCalleeEditor.getDocument();
ApplicationManager.getApplication().runWriteAction(() -> {
@@ -150,7 +149,7 @@ public abstract class CallerChooserBase<M extends PsiElement> extends DialogWrap
calleeDocument.setText(calleeText);
});
final M caller = callerNode.getMethod();
final M caller = callerNode.getMember();
final PsiElement callee = parentNode != null ? parentNode.getElementToSearch() : null;
if (caller != null && caller.isPhysical() && callee != null) {
HighlightManager highlighter = HighlightManager.getInstance(myProject);
@@ -164,11 +163,11 @@ public abstract class CallerChooserBase<M extends PsiElement> extends DialogWrap
}
}
protected MethodNodeBase<M> getCalleeNode(MethodNodeBase<M> node) {
return (MethodNodeBase<M>)node.getParent();
protected MemberNodeBase<M> getCalleeNode(MemberNodeBase<M> node) {
return (MemberNodeBase<M>)node.getParent();
}
protected MethodNodeBase<M> getCallerNode(MethodNodeBase<M> node) {
protected MemberNodeBase<M> getCallerNode(MemberNodeBase<M> node) {
return node;
}
@@ -250,8 +249,8 @@ public abstract class CallerChooserBase<M extends PsiElement> extends DialogWrap
boolean leaf,
int row,
boolean hasFocus) {
if (value instanceof MethodNodeBase) {
((MethodNodeBase)value).customizeRenderer(getTextRenderer());
if (value instanceof MemberNodeBase) {
((MemberNodeBase)value).customizeRenderer(getTextRenderer());
}
}
};
@@ -262,19 +261,19 @@ public abstract class CallerChooserBase<M extends PsiElement> extends DialogWrap
return tree;
}
protected M getTopMethod() {
protected M getTopMember() {
return myMethod;
}
private void getSelectedMethods(Set<M> methods) {
MethodNodeBase<M> node = myRoot;
MemberNodeBase<M> node = myRoot;
getSelectedMethodsInner(node, methods);
methods.remove(node.getMethod());
methods.remove(node.getMember());
}
private void getSelectedMethodsInner(final MethodNodeBase<M> node, final Set<M> allMethods) {
private void getSelectedMethodsInner(final MemberNodeBase<M> node, final Set<M> allMethods) {
if (node.isChecked()) {
M method = node.getMethod();
M method = node.getMember();
final M[] superMethods = method == myMethod ? null : findDeepestSuperMethods(method);
if (superMethods == null || superMethods.length == 0) {
allMethods.add(method);
@@ -285,23 +284,23 @@ public abstract class CallerChooserBase<M extends PsiElement> extends DialogWrap
final Enumeration children = node.children();
while (children.hasMoreElements()) {
getSelectedMethodsInner((MethodNodeBase)children.nextElement(), allMethods);
getSelectedMethodsInner((MemberNodeBase)children.nextElement(), allMethods);
}
}
}
protected Set<MethodNodeBase<M>> getSelectedNodes() {
final Set<MethodNodeBase<M>> nodes = new LinkedHashSet<>();
protected Set<MemberNodeBase<M>> getSelectedNodes() {
final Set<MemberNodeBase<M>> nodes = new LinkedHashSet<>();
collectSelectedNodes(myRoot, nodes);
return nodes;
}
private void collectSelectedNodes(final MethodNodeBase<M> node, final Set<MethodNodeBase<M>> nodes) {
private void collectSelectedNodes(final MemberNodeBase<M> node, final Set<MemberNodeBase<M>> nodes) {
if (node.isChecked()) {
nodes.add(node);
final Enumeration children = node.children();
while (children.hasMoreElements()) {
collectSelectedNodes((MethodNodeBase)children.nextElement(), nodes);
collectSelectedNodes((MemberNodeBase)children.nextElement(), nodes);
}
}
}
@@ -0,0 +1,142 @@
/*
* Copyright 2000-2017 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.changeSignature;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.progress.ProgressManager;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.*;
import com.intellij.psi.PsiElement;
import com.intellij.refactoring.RefactoringBundle;
import com.intellij.ui.CheckedTreeNode;
import com.intellij.ui.ColoredTreeCellRenderer;
import com.intellij.util.containers.ContainerUtil;
import javax.swing.*;
import javax.swing.tree.TreeNode;
import java.util.*;
public abstract class MemberNodeBase<M extends PsiElement> extends CheckedTreeNode {
protected final M myMethod;
protected final Set<M> myCalled;
protected final Project myProject;
protected final Runnable myCancelCallback;
private boolean myOldChecked;
protected abstract MemberNodeBase<M> createNode(M caller, HashSet<M> called);
protected abstract List<M> computeCallers();
protected abstract void customizeRendererText(ColoredTreeCellRenderer renderer);
protected Condition<M> getFilter() {
return Conditions.alwaysTrue();
}
protected MemberNodeBase(final M method, Set<M> called, Project project, Runnable cancelCallback) {
super(method);
myMethod = method;
myCalled = called;
myProject = project;
myCancelCallback = cancelCallback;
isChecked = false;
}
//IMPORTANT: do not build children in children()
private void buildChildren() {
if (children == null) {
final List<M> callers = findCallers();
children = new Vector(callers.size());
for (M caller : callers) {
final HashSet<M> called = new HashSet<>(myCalled);
called.add(getMember());
final MemberNodeBase<M> child = createNode(caller, called);
children.add(child);
child.parent = this;
}
}
}
@Override
public TreeNode getChildAt(int index) {
buildChildren();
return super.getChildAt(index);
}
@Override
public int getChildCount() {
buildChildren();
return super.getChildCount();
}
@Override
public boolean isLeaf() {
if (children == null) {
return false;
}
return super.isLeaf();
}
@Override
public int getIndex(TreeNode aChild) {
buildChildren();
return super.getIndex(aChild);
}
private List<M> findCallers() {
if (getMember() == null) return Collections.emptyList();
final Ref<List<M>> callers = new Ref<>();
if (!ProgressManager.getInstance().runProcessWithProgressSynchronously(() -> ApplicationManager.getApplication().runReadAction(() -> callers.set(ContainerUtil.filter(computeCallers(), getFilter()))), RefactoringBundle.message("caller.chooser.looking.for.callers"), true, myProject)) {
myCancelCallback.run();
return Collections.emptyList();
}
return callers.get();
}
public void customizeRenderer(ColoredTreeCellRenderer renderer) {
if (getMember() == null) return;
final int flags = Iconable.ICON_FLAG_VISIBILITY | Iconable.ICON_FLAG_READ_STATUS;
renderer.setIcon(ApplicationManager.getApplication().runReadAction(new Computable<Icon>() {
@Override
public Icon compute() {
return getMember().getIcon(flags);
}
}));
customizeRendererText(renderer);
}
@Override
public void setEnabled(final boolean enabled) {
super.setEnabled(enabled);
if (!enabled) {
myOldChecked = isChecked();
setChecked(false);
}
else {
setChecked(myOldChecked);
}
}
public M getMember() {
return myMethod;
}
public PsiElement getElementToSearch() {
return getMember();
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2000-2014 JetBrains s.r.o.
* Copyright 2000-2017 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.
@@ -15,131 +15,24 @@
*/
package com.intellij.refactoring.changeSignature;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.progress.ProgressManager;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Computable;
import com.intellij.openapi.util.Condition;
import com.intellij.openapi.util.Iconable;
import com.intellij.openapi.util.Ref;
import com.intellij.psi.PsiElement;
import com.intellij.refactoring.RefactoringBundle;
import com.intellij.ui.CheckedTreeNode;
import com.intellij.ui.ColoredTreeCellRenderer;
import com.intellij.util.containers.ContainerUtil;
import javax.swing.*;
import javax.swing.tree.TreeNode;
import java.util.*;
import java.util.Set;
public abstract class MethodNodeBase<M extends PsiElement> extends CheckedTreeNode {
protected final M myMethod;
protected final Set<M> myCalled;
protected final Project myProject;
protected final Runnable myCancelCallback;
private boolean myOldChecked;
protected abstract MethodNodeBase<M> createNode(M caller, HashSet<M> called);
protected abstract List<M> computeCallers();
protected abstract void customizeRendererText(ColoredTreeCellRenderer renderer);
protected Condition<M> getFilter() {
return Condition.TRUE;
}
protected MethodNodeBase(final M method, Set<M> called, Project project, Runnable cancelCallback) {
super(method);
myMethod = method;
myCalled = called;
myProject = project;
myCancelCallback = cancelCallback;
isChecked = false;
}
//IMPORTANT: do not build children in children()
private void buildChildren() {
if (children == null) {
final List<M> callers = findCallers();
children = new Vector(callers.size());
for (M caller : callers) {
final HashSet<M> called = new HashSet<>(myCalled);
called.add(myMethod);
final MethodNodeBase<M> child = createNode(caller, called);
children.add(child);
child.parent = this;
}
}
}
@Override
public TreeNode getChildAt(int index) {
buildChildren();
return super.getChildAt(index);
}
@Override
public int getChildCount() {
buildChildren();
return super.getChildCount();
}
@Override
public boolean isLeaf() {
if (children == null) {
return false;
}
return super.isLeaf();
}
@Override
public int getIndex(TreeNode aChild) {
buildChildren();
return super.getIndex(aChild);
}
private List<M> findCallers() {
if (myMethod == null) return Collections.emptyList();
final Ref<List<M>> callers = new Ref<>();
if (!ProgressManager.getInstance().runProcessWithProgressSynchronously(() -> ApplicationManager.getApplication().runReadAction(() -> callers.set(ContainerUtil.filter(computeCallers(), getFilter()))), RefactoringBundle.message("caller.chooser.looking.for.callers"), true, myProject)) {
myCancelCallback.run();
return Collections.emptyList();
}
return callers.get();
}
public void customizeRenderer(ColoredTreeCellRenderer renderer) {
if (myMethod == null) return;
final int flags = Iconable.ICON_FLAG_VISIBILITY | Iconable.ICON_FLAG_READ_STATUS;
renderer.setIcon(ApplicationManager.getApplication().runReadAction(new Computable<Icon>() {
@Override
public Icon compute() {
return myMethod.getIcon(flags);
}
}));
customizeRendererText(renderer);
}
@Override
public void setEnabled(final boolean enabled) {
super.setEnabled(enabled);
if (!enabled) {
myOldChecked = isChecked();
setChecked(false);
}
else {
setChecked(myOldChecked);
}
/**
* @deprecated to be removed in IDEA 2019.1
*/
@Deprecated
public abstract class MethodNodeBase<M extends PsiElement> extends MemberNodeBase<M> {
protected MethodNodeBase(M method,
Set<M> called,
Project project,
Runnable cancelCallback) {
super(method, called, project, cancelCallback);
}
public M getMethod() {
return myMethod;
return getMember();
}
public PsiElement getElementToSearch() {
return getMethod();
}
}