mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
deep delete parameter in method call hierarchy
This commit is contained in:
+35
-4
@@ -39,20 +39,20 @@ 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.safeDelete.usageInfo.*;
|
||||
import com.intellij.refactoring.util.RefactoringMessageUtil;
|
||||
import com.intellij.refactoring.util.RefactoringUIUtil;
|
||||
import com.intellij.usageView.UsageInfo;
|
||||
import com.intellij.usageView.UsageViewUtil;
|
||||
import com.intellij.usages.*;
|
||||
import com.intellij.util.ArrayUtil;
|
||||
import com.intellij.util.IncorrectOperationException;
|
||||
import com.intellij.util.Processor;
|
||||
import com.intellij.util.*;
|
||||
import com.intellij.util.containers.HashMap;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.HashSet;
|
||||
|
||||
public class JavaSafeDeleteProcessor extends SafeDeleteProcessorDelegateBase {
|
||||
private static final Logger LOG = Logger.getInstance("#com.intellij.refactoring.safeDelete.JavaSafeDeleteProcessor");
|
||||
@@ -240,8 +240,9 @@ public class JavaSafeDeleteProcessor extends SafeDeleteProcessorDelegateBase {
|
||||
|
||||
@Nullable
|
||||
public UsageInfo[] preprocessUsages(final Project project, final UsageInfo[] usages) {
|
||||
ArrayList<UsageInfo> result = new ArrayList<UsageInfo>();
|
||||
final ArrayList<UsageInfo> result = new ArrayList<UsageInfo>();
|
||||
ArrayList<UsageInfo> overridingMethods = new ArrayList<UsageInfo>();
|
||||
ArrayList<UsageInfo> delegatingParams = new ArrayList<UsageInfo>();
|
||||
for (UsageInfo usage : usages) {
|
||||
if (usage.isNonCodeUsage) {
|
||||
result.add(usage);
|
||||
@@ -249,6 +250,9 @@ public class JavaSafeDeleteProcessor extends SafeDeleteProcessorDelegateBase {
|
||||
else if (usage instanceof SafeDeleteOverridingMethodUsageInfo) {
|
||||
overridingMethods.add(usage);
|
||||
}
|
||||
else if (usage instanceof SafeDeleteParameterCallHierarchyUsageInfo) {
|
||||
delegatingParams.add(usage);
|
||||
}
|
||||
else {
|
||||
result.add(usage);
|
||||
}
|
||||
@@ -267,6 +271,21 @@ public class JavaSafeDeleteProcessor extends SafeDeleteProcessorDelegateBase {
|
||||
}
|
||||
}
|
||||
|
||||
if (delegatingParams.size() == 1) {
|
||||
final SafeDeleteParameterCallHierarchyUsageInfo parameterHierarchyUsageInfo = (SafeDeleteParameterCallHierarchyUsageInfo)delegatingParams.get(0);
|
||||
if (ApplicationManager.getApplication().isUnitTestMode()) {
|
||||
result.addAll(delegatingParams);
|
||||
} else {
|
||||
final PsiMethod method = parameterHierarchyUsageInfo.getCalledMethod();
|
||||
final PsiParameter parameter = parameterHierarchyUsageInfo.getReferencedElement();
|
||||
final int parameterIndex = method.getParameterList().getParameterIndex(parameter);
|
||||
final JavaCallerChooser chooser = new SafeDeleteJavaCallerChooser(method, project, parameterIndex, result);
|
||||
if (!chooser.showAndGet()) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result.toArray(new UsageInfo[result.size()]);
|
||||
}
|
||||
|
||||
@@ -709,12 +728,24 @@ public class JavaSafeDeleteProcessor extends SafeDeleteProcessorDelegateBase {
|
||||
|
||||
private static void findParameterUsages(final PsiParameter parameter, final List<UsageInfo> usages) {
|
||||
final PsiMethod method = (PsiMethod)parameter.getDeclarationScope();
|
||||
final int parameterIndex = method.getParameterList().getParameterIndex(parameter);
|
||||
//search for refs to current method only, do not search for refs to overriding methods, they'll be searched separately
|
||||
ReferencesSearch.search(method).forEach(new Processor<PsiReference>() {
|
||||
public boolean process(final PsiReference reference) {
|
||||
PsiElement element = reference.getElement();
|
||||
if (element != null) {
|
||||
JavaSafeDeleteDelegate.EP.forLanguage(element.getLanguage()).createUsageInfoForParameter(reference, usages, parameter, method);
|
||||
if (!parameter.isVarArgs()) {
|
||||
final PsiParameter paramInCaller = SafeDeleteJavaCallerChooser.isTheOnlyOneParameterUsage(element.getParent(), parameterIndex, method);
|
||||
if (paramInCaller != null) {
|
||||
if (ApplicationManager.getApplication().isUnitTestMode()) {
|
||||
usages.add(new SafeDeleteParameterCallHierarchyUsageInfo((PsiMethod)paramInCaller.getDeclarationScope(), paramInCaller));
|
||||
}
|
||||
else {
|
||||
usages.add(new SafeDeleteParameterCallHierarchyUsageInfo(method, parameter));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
+202
@@ -0,0 +1,202 @@
|
||||
/*
|
||||
* 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.safeDelete;
|
||||
|
||||
import com.intellij.openapi.application.ApplicationManager;
|
||||
import com.intellij.openapi.progress.ProgressManager;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.util.Condition;
|
||||
import com.intellij.openapi.util.Ref;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.psi.search.LocalSearchScope;
|
||||
import com.intellij.psi.search.searches.ReferencesSearch;
|
||||
import com.intellij.psi.util.PsiUtil;
|
||||
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.SafeDeleteParameterCallHierarchyUsageInfo;
|
||||
import com.intellij.usageView.UsageInfo;
|
||||
import com.intellij.util.Consumer;
|
||||
import com.intellij.util.Processor;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
class SafeDeleteJavaCallerChooser extends JavaCallerChooser {
|
||||
private final PsiMethod myMethod;
|
||||
private final Project myProject;
|
||||
private final int myParameterIndex;
|
||||
private final ArrayList<UsageInfo> myResult;
|
||||
|
||||
public SafeDeleteJavaCallerChooser(PsiMethod method, Project project, int parameterIndex, ArrayList<UsageInfo> result) {
|
||||
super(method, project, "Select Methods To Propagate Parameter Deletion", null, Consumer.EMPTY_CONSUMER);
|
||||
myMethod = method;
|
||||
myProject = project;
|
||||
myParameterIndex = parameterIndex;
|
||||
myResult = result;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected JavaMethodNode createTreeNode(PsiMethod nodeMethod,
|
||||
com.intellij.util.containers.HashSet<PsiMethod> called,
|
||||
Runnable cancelCallback) {
|
||||
return new SafeDeleteJavaMethodNode(nodeMethod, called, cancelCallback, myParameterIndex, nodeMethod != null ? nodeMethod.getProject() : myProject);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doOKAction() {
|
||||
final List<UsageInfo> foreignMethodUsages = new ArrayList<UsageInfo>();
|
||||
final Runnable runnable = new Runnable() {
|
||||
public void run() {
|
||||
final Set<MethodNodeBase<PsiMethod>> nodes = getSelectedNodes();
|
||||
for (MethodNodeBase<PsiMethod> node : nodes) {
|
||||
final SafeDeleteJavaMethodNode methodNode = (SafeDeleteJavaMethodNode)node;
|
||||
final PsiMethod nodeMethod = methodNode.myCurrentMethod;
|
||||
if (nodeMethod.equals(myMethod)) continue;
|
||||
final PsiParameter parameter = nodeMethod.getParameterList().getParameters()[methodNode.myParameterIdx];
|
||||
foreignMethodUsages.add(new SafeDeleteParameterCallHierarchyUsageInfo(nodeMethod, parameter));
|
||||
ReferencesSearch.search(nodeMethod).forEach(new Processor<PsiReference>() {
|
||||
public boolean process(final PsiReference reference) {
|
||||
final PsiElement element = reference.getElement();
|
||||
if (element != null) {
|
||||
JavaSafeDeleteDelegate.EP.forLanguage(element.getLanguage())
|
||||
.createUsageInfoForParameter(reference, foreignMethodUsages, parameter, nodeMethod);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
if (ProgressManager.getInstance().runProcessWithProgressSynchronously(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
ApplicationManager.getApplication().runReadAction(runnable);
|
||||
}
|
||||
}, "Search for caller method usages...", true, myProject)) {
|
||||
myResult.addAll(foreignMethodUsages);
|
||||
}
|
||||
super.doOKAction();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return parameter if it is used inside method only as argument in nodeMethod call at parameterIndex
|
||||
*/
|
||||
static PsiParameter isTheOnlyOneParameterUsage(PsiElement call, int parameterIndex, final PsiMethod nodeMethod) {
|
||||
if (call instanceof PsiCallExpression) {
|
||||
final PsiExpressionList argumentList = ((PsiCallExpression)call).getArgumentList();
|
||||
if (argumentList != null) {
|
||||
final PsiExpression[] expressions = argumentList.getExpressions();
|
||||
if (expressions.length > parameterIndex) {
|
||||
final PsiExpression expression = PsiUtil.deparenthesizeExpression(expressions[parameterIndex]);
|
||||
if (expression instanceof PsiReferenceExpression) {
|
||||
final PsiElement resolve = ((PsiReferenceExpression)expression).resolve();
|
||||
if (resolve instanceof PsiParameter && !((PsiParameter)resolve).isVarArgs()) {
|
||||
final PsiElement scope = ((PsiParameter)resolve).getDeclarationScope();
|
||||
if (scope instanceof PsiMethod) {
|
||||
if (ReferencesSearch.search(resolve, new LocalSearchScope(scope)).forEach(new Processor<PsiReference>() {
|
||||
@Override
|
||||
public boolean process(PsiReference reference) {
|
||||
final PsiElement element = reference.getElement();
|
||||
if (element instanceof PsiReferenceExpression) {
|
||||
final PsiElement parent = element.getParent();
|
||||
if (parent instanceof PsiExpressionList) {
|
||||
final PsiElement gParent = parent.getParent();
|
||||
if (gParent instanceof PsiCallExpression &&
|
||||
nodeMethod.equals(((PsiCallExpression)gParent).resolveMethod())) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
})) {
|
||||
return (PsiParameter)resolve;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static class SafeDeleteJavaMethodNode extends JavaMethodNode {
|
||||
|
||||
private final PsiMethod myCurrentMethod;
|
||||
private final int myParameterIdx;
|
||||
|
||||
public SafeDeleteJavaMethodNode(PsiMethod currentMethod,
|
||||
HashSet<PsiMethod> called,
|
||||
Runnable cancelCallback,
|
||||
int idx,
|
||||
Project project) {
|
||||
super(currentMethod, called, project, cancelCallback);
|
||||
myCurrentMethod = currentMethod;
|
||||
myParameterIdx = idx;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected MethodNodeBase<PsiMethod> createNode(PsiMethod caller, HashSet<PsiMethod> called) {
|
||||
return new SafeDeleteJavaMethodNode(caller, called, myCancelCallback, getParameterIndex(caller), myProject);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Condition<PsiMethod> getFilter() {
|
||||
return new Condition<PsiMethod>() {
|
||||
@Override
|
||||
public boolean value(PsiMethod method) {
|
||||
return getParameter(method) != null;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private PsiParameter getParameter(PsiMethod caller) {
|
||||
|
||||
//do not change hierarchy
|
||||
if (caller.findDeepestSuperMethods().length > 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
//find first method call
|
||||
final Ref<PsiParameter> ref = new Ref<PsiParameter>();
|
||||
ReferencesSearch.search(myCurrentMethod, new LocalSearchScope(caller)).forEach(new Processor<PsiReference>() {
|
||||
@Override
|
||||
public boolean process(PsiReference reference) {
|
||||
final PsiElement element = reference.getElement();
|
||||
if (element instanceof PsiReferenceExpression) {
|
||||
final PsiElement elementParent = element.getParent();
|
||||
if (elementParent instanceof PsiCallExpression) {
|
||||
ref.set(isTheOnlyOneParameterUsage(elementParent, myParameterIdx, myCurrentMethod));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
});
|
||||
return ref.get();
|
||||
}
|
||||
|
||||
private int getParameterIndex(PsiMethod caller) {
|
||||
final PsiParameter parameter = getParameter(caller);
|
||||
return parameter != null ? caller.getParameterList().getParameterIndex(parameter) : -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
+46
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* 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.safeDelete.usageInfo;
|
||||
|
||||
import com.intellij.psi.PsiMethod;
|
||||
import com.intellij.psi.PsiParameter;
|
||||
import com.intellij.util.IncorrectOperationException;
|
||||
|
||||
public class SafeDeleteParameterCallHierarchyUsageInfo extends SafeDeleteUsageInfo implements SafeDeleteCustomUsageInfo {
|
||||
|
||||
private PsiMethod myCalledMethod;
|
||||
|
||||
public SafeDeleteParameterCallHierarchyUsageInfo(PsiMethod calledMethod, PsiParameter parameter) {
|
||||
super(calledMethod, parameter);
|
||||
myCalledMethod = calledMethod;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PsiParameter getReferencedElement() {
|
||||
return (PsiParameter)super.getReferencedElement();
|
||||
}
|
||||
|
||||
public void performRefactoring() throws IncorrectOperationException {
|
||||
final PsiParameter parameter = getReferencedElement();
|
||||
if (parameter != null && parameter.isValid()) {
|
||||
parameter.delete();
|
||||
}
|
||||
}
|
||||
|
||||
public PsiMethod getCalledMethod() {
|
||||
return myCalledMethod;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
class Test {
|
||||
void foo(int i) {
|
||||
bar(i);
|
||||
bar(i);
|
||||
}
|
||||
|
||||
void bar(int <caret>i){}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
class Test {
|
||||
void foo() {
|
||||
bar();
|
||||
bar();
|
||||
}
|
||||
|
||||
void bar(){}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
class Test {
|
||||
void foo(int i) {
|
||||
bar(i);
|
||||
baz(i);
|
||||
}
|
||||
|
||||
void bar(int <caret>i){}
|
||||
void baz(int i){}
|
||||
}
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
class Test {
|
||||
void foo(int i) {
|
||||
bar();
|
||||
baz(i);
|
||||
}
|
||||
|
||||
void bar(){}
|
||||
void baz(int i){}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
interface I {
|
||||
void foo(int i);
|
||||
}
|
||||
class Test implements I {
|
||||
public void foo(int i) {
|
||||
bar(i);
|
||||
bar(i);
|
||||
}
|
||||
|
||||
void bar(int <caret>i){}
|
||||
}
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
interface I {
|
||||
void foo(int i);
|
||||
}
|
||||
class Test implements I {
|
||||
public void foo(int i) {
|
||||
bar();
|
||||
bar();
|
||||
}
|
||||
|
||||
void bar(){}
|
||||
}
|
||||
@@ -76,6 +76,18 @@ public class SafeDeleteTest extends MultiFileTestCase {
|
||||
doSingleFileTest();
|
||||
}
|
||||
|
||||
public void testDeepDeleteParameterSimple() throws Exception {
|
||||
doSingleFileTest();
|
||||
}
|
||||
|
||||
public void testImpossibleToDeepDeleteParameter() throws Exception {
|
||||
doSingleFileTest();
|
||||
}
|
||||
|
||||
public void testToDeepDeleteParameterOverriders() throws Exception {
|
||||
doSingleFileTest();
|
||||
}
|
||||
|
||||
public void testParameterInHierarchy() throws Exception {
|
||||
doTest("C2");
|
||||
}
|
||||
|
||||
+17
-4
@@ -53,10 +53,7 @@ import javax.swing.event.TreeSelectionListener;
|
||||
import javax.swing.tree.TreePath;
|
||||
import javax.swing.tree.TreeSelectionModel;
|
||||
import java.awt.*;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Enumeration;
|
||||
import java.util.Set;
|
||||
import java.util.*;
|
||||
|
||||
public abstract class CallerChooserBase<M extends PsiElement> extends DialogWrapper {
|
||||
private final M myMethod;
|
||||
@@ -288,6 +285,22 @@ public abstract class CallerChooserBase<M extends PsiElement> extends DialogWrap
|
||||
}
|
||||
}
|
||||
|
||||
protected Set<MethodNodeBase<M>> getSelectedNodes() {
|
||||
final Set<MethodNodeBase<M>> nodes = new LinkedHashSet<MethodNodeBase<M>>();
|
||||
collectSelectedNodes(myRoot, nodes);
|
||||
return nodes;
|
||||
}
|
||||
|
||||
private void collectSelectedNodes(final MethodNodeBase<M> node, final Set<MethodNodeBase<M>> nodes) {
|
||||
if (node.isChecked()) {
|
||||
nodes.add(node);
|
||||
final Enumeration children = node.children();
|
||||
while (children.hasMoreElements()) {
|
||||
collectSelectedNodes((MethodNodeBase)children.nextElement(), nodes);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doOKAction() {
|
||||
final Set<M> selectedMethods = new HashSet<M>();
|
||||
|
||||
@@ -18,12 +18,14 @@ 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.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.tree.TreeNode;
|
||||
import java.util.*;
|
||||
@@ -41,6 +43,10 @@ public abstract class MethodNodeBase<M extends PsiElement> extends CheckedTreeNo
|
||||
|
||||
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;
|
||||
@@ -92,7 +98,7 @@ public abstract class MethodNodeBase<M extends PsiElement> extends CheckedTreeNo
|
||||
ApplicationManager.getApplication().runReadAction(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
callers.set(computeCallers());
|
||||
callers.set(ContainerUtil.filter(computeCallers(), getFilter()));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user