mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-01-08 15:09:39 +07:00
inline superclass refactoring: allow to collapse only one inheritance (IDEA-93319)
This commit is contained in:
@@ -81,6 +81,7 @@ public class JavaRefactoringSettings implements PersistentStateComponent<JavaRef
|
||||
public int PULL_UP_MEMBERS_JAVADOC;
|
||||
public boolean PUSH_DOWN_PREVIEW_USAGES;
|
||||
public boolean INLINE_METHOD_THIS;
|
||||
public boolean INLINE_SUPER_CLASS_THIS;
|
||||
public boolean INLINE_FIELD_THIS;
|
||||
//public boolean INHERITANCE_TO_DELEGATION_PREVIEW_USAGES;
|
||||
public boolean INHERITANCE_TO_DELEGATION_DELEGATE_OTHER;
|
||||
|
||||
@@ -24,22 +24,27 @@ import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
import com.intellij.psi.PsiClass;
|
||||
import com.intellij.refactoring.JavaRefactoringSettings;
|
||||
import com.intellij.refactoring.RefactoringBundle;
|
||||
import com.intellij.refactoring.inline.InlineOptionsDialog;
|
||||
import com.intellij.refactoring.ui.DocCommentPanel;
|
||||
import com.intellij.refactoring.ui.RefactoringDialog;
|
||||
import com.intellij.ui.IdeBorderFactory;
|
||||
import com.intellij.util.Function;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.awt.*;
|
||||
|
||||
public class InlineSuperClassRefactoringDialog extends RefactoringDialog{
|
||||
public class InlineSuperClassRefactoringDialog extends InlineOptionsDialog {
|
||||
private final PsiClass mySuperClass;
|
||||
private final PsiClass myCurrentInheritor;
|
||||
private final PsiClass[] myTargetClasses;
|
||||
private final DocCommentPanel myDocPanel;
|
||||
|
||||
protected InlineSuperClassRefactoringDialog(@NotNull Project project, PsiClass superClass, final PsiClass... targetClasses) {
|
||||
super(project, false);
|
||||
protected InlineSuperClassRefactoringDialog(@NotNull Project project, PsiClass superClass, PsiClass currentInheritor, final PsiClass... targetClasses) {
|
||||
super(project, false, superClass);
|
||||
mySuperClass = superClass;
|
||||
myCurrentInheritor = currentInheritor;
|
||||
myInvokedOnReference = currentInheritor != null;
|
||||
myTargetClasses = targetClasses;
|
||||
myDocPanel = new DocCommentPanel("JavaDoc for inlined members");
|
||||
myDocPanel.setPolicy(JavaRefactoringSettings.getInstance().PULL_UP_MEMBERS_JAVADOC);
|
||||
@@ -48,26 +53,64 @@ public class InlineSuperClassRefactoringDialog extends RefactoringDialog{
|
||||
}
|
||||
|
||||
protected void doAction() {
|
||||
invokeRefactoring(new InlineSuperClassRefactoringProcessor(getProject(), mySuperClass, myDocPanel.getPolicy(), myTargetClasses));
|
||||
JavaRefactoringSettings settings = JavaRefactoringSettings.getInstance();
|
||||
if(myRbInlineThisOnly.isEnabled() && myRbInlineAll.isEnabled()) {
|
||||
settings.INLINE_SUPER_CLASS_THIS = isInlineThisOnly();
|
||||
}
|
||||
invokeRefactoring(new InlineSuperClassRefactoringProcessor(getProject(), isInlineThisOnly() ? myCurrentInheritor : null, mySuperClass, myDocPanel.getPolicy(), myTargetClasses));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected JComponent createNorthPanel() {
|
||||
return myDocPanel;
|
||||
return null;
|
||||
}
|
||||
|
||||
protected JComponent createCenterPanel() {
|
||||
final JLabel label = new JLabel("<html>Inline \'" +
|
||||
final JLabel label = new JLabel("<html>Super class \'" +
|
||||
mySuperClass.getQualifiedName() +
|
||||
"\' to " +
|
||||
"\' inheritors: " +
|
||||
(myTargetClasses.length > 1 ? " <br> \'" : "\'") +
|
||||
StringUtil.join(myTargetClasses, new Function<PsiClass, String>() {
|
||||
public String fun(final PsiClass psiClass) {
|
||||
return psiClass.getQualifiedName();
|
||||
}
|
||||
}, "\',<br> \'") +
|
||||
public String fun(final PsiClass psiClass) {
|
||||
return psiClass.getQualifiedName();
|
||||
}
|
||||
}, "\',<br> \'") +
|
||||
"\'</html>");
|
||||
label.setBorder(IdeBorderFactory.createEmptyBorder(5, 5, 5, 5));
|
||||
return label;
|
||||
final JPanel panel = new JPanel(new GridBagLayout());
|
||||
final GridBagConstraints gc =
|
||||
new GridBagConstraints(0, GridBagConstraints.RELATIVE, 1, 1, 1, 0, GridBagConstraints.NORTHWEST, GridBagConstraints.HORIZONTAL,
|
||||
new Insets(0, 0, 0, 0), 0, 0);
|
||||
panel.add(myDocPanel, gc);
|
||||
panel.add(label, gc);
|
||||
gc.weighty = 1;
|
||||
gc.fill = GridBagConstraints.BOTH;
|
||||
panel.add(super.createCenterPanel(), gc);
|
||||
return panel;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getNameLabelText() {
|
||||
return "Class " + mySuperClass.getQualifiedName();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getBorderTitle() {
|
||||
return "Inline";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getInlineAllText() {
|
||||
return RefactoringBundle.message("all.references.and.remove.super.class");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getInlineThisText() {
|
||||
return RefactoringBundle.message("this.reference.only.and.keep.super.class");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isInlineThis() {
|
||||
return JavaRefactoringSettings.getInstance().INLINE_SUPER_CLASS_THIS;
|
||||
}
|
||||
}
|
||||
@@ -20,12 +20,11 @@
|
||||
*/
|
||||
package com.intellij.refactoring.inlineSuperClass;
|
||||
|
||||
import com.intellij.codeInsight.TargetElementUtilBase;
|
||||
import com.intellij.lang.StdLanguages;
|
||||
import com.intellij.openapi.editor.Editor;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.psi.PsiAnonymousClass;
|
||||
import com.intellij.psi.PsiClass;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.psi.search.searches.DirectClassInheritorsSearch;
|
||||
import com.intellij.psi.util.PsiTreeUtil;
|
||||
import com.intellij.refactoring.inline.JavaInlineActionHandler;
|
||||
@@ -67,6 +66,23 @@ public class InlineSuperClassRefactoringHandler extends JavaInlineActionHandler
|
||||
}
|
||||
}
|
||||
|
||||
new InlineSuperClassRefactoringDialog(project, superClass, inheritors.toArray(new PsiClass[inheritors.size()])).show();
|
||||
PsiClass chosen = null;
|
||||
PsiReference reference = editor != null ? TargetElementUtilBase.findReference(editor, editor.getCaretModel().getOffset()) : null;
|
||||
if (reference != null) {
|
||||
final PsiElement resolve = reference.resolve();
|
||||
if (resolve == superClass) {
|
||||
final PsiElement referenceElement = reference.getElement();
|
||||
if (referenceElement != null) {
|
||||
final PsiElement parent = referenceElement.getParent();
|
||||
if (parent instanceof PsiReferenceList) {
|
||||
final PsiElement gParent = parent.getParent();
|
||||
if (gParent instanceof PsiClass && inheritors.contains(gParent)) {
|
||||
chosen = (PsiClass)gParent;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
new InlineSuperClassRefactoringDialog(project, superClass, chosen, inheritors.toArray(new PsiClass[inheritors.size()])).show();
|
||||
}
|
||||
}
|
||||
@@ -26,6 +26,7 @@ import com.intellij.openapi.util.Ref;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.psi.search.searches.ReferencesSearch;
|
||||
import com.intellij.psi.util.PsiTreeUtil;
|
||||
import com.intellij.psi.util.PsiTypesUtil;
|
||||
import com.intellij.psi.util.PsiUtil;
|
||||
import com.intellij.psi.util.TypeConversionUtil;
|
||||
import com.intellij.refactoring.inlineSuperClass.usageInfo.*;
|
||||
@@ -39,11 +40,13 @@ import com.intellij.refactoring.util.classMembers.MemberInfo;
|
||||
import com.intellij.refactoring.util.classMembers.MemberInfoStorage;
|
||||
import com.intellij.usageView.UsageInfo;
|
||||
import com.intellij.usageView.UsageViewDescriptor;
|
||||
import com.intellij.util.ArrayUtilRt;
|
||||
import com.intellij.util.IncorrectOperationException;
|
||||
import com.intellij.util.Processor;
|
||||
import com.intellij.util.containers.HashMap;
|
||||
import com.intellij.util.containers.MultiMap;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
@@ -51,16 +54,18 @@ import java.util.Map;
|
||||
public class InlineSuperClassRefactoringProcessor extends FixableUsagesRefactoringProcessor {
|
||||
public static final Logger LOG = Logger.getInstance("#" + InlineSuperClassRefactoringProcessor.class.getName());
|
||||
|
||||
private final PsiClass myCurrentInheritor;
|
||||
private final PsiClass mySuperClass;
|
||||
private final int myPolicy;
|
||||
private final PsiClass[] myTargetClasses;
|
||||
private final MemberInfo[] myMemberInfos;
|
||||
|
||||
public InlineSuperClassRefactoringProcessor(Project project, PsiClass superClass, int policy, final PsiClass... targetClasses) {
|
||||
public InlineSuperClassRefactoringProcessor(Project project, PsiClass currentInheritor, PsiClass superClass, int policy, final PsiClass... targetClasses) {
|
||||
super(project);
|
||||
myCurrentInheritor = currentInheritor;
|
||||
mySuperClass = superClass;
|
||||
myPolicy = policy;
|
||||
myTargetClasses = targetClasses;
|
||||
myTargetClasses = currentInheritor != null ? new PsiClass[] {currentInheritor} : targetClasses;
|
||||
MemberInfoStorage memberInfoStorage = new MemberInfoStorage(mySuperClass, new MemberInfo.Filter<PsiMember>() {
|
||||
public boolean includeMember(PsiMember element) {
|
||||
return !(element instanceof PsiClass) || PsiTreeUtil.isAncestor(mySuperClass, element, true);
|
||||
@@ -88,6 +93,21 @@ public class InlineSuperClassRefactoringProcessor extends FixableUsagesRefactori
|
||||
public boolean process(final PsiReference reference) {
|
||||
final PsiElement element = reference.getElement();
|
||||
if (element instanceof PsiJavaCodeReferenceElement) {
|
||||
if (myCurrentInheritor != null) {
|
||||
final PsiElement parent = element.getParent();
|
||||
if (parent instanceof PsiReferenceList) {
|
||||
final PsiElement pparent = parent.getParent();
|
||||
if (pparent instanceof PsiClass) {
|
||||
final PsiClass inheritor = (PsiClass)pparent;
|
||||
if (parent.equals(inheritor.getExtendsList()) || parent.equals(inheritor.getImplementsList())) {
|
||||
if (myCurrentInheritor.equals(inheritor)) {
|
||||
usages.add(new ReplaceExtendsListUsageInfo((PsiJavaCodeReferenceElement)element, mySuperClass, inheritor));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
final PsiImportStaticStatement staticImportStatement = PsiTreeUtil.getParentOfType(element, PsiImportStaticStatement.class);
|
||||
if (staticImportStatement != null) {
|
||||
usages.add(new ReplaceStaticImportUsageInfo(staticImportStatement, myTargetClasses));
|
||||
@@ -204,10 +224,56 @@ public class InlineSuperClassRefactoringProcessor extends FixableUsagesRefactori
|
||||
for (PsiElement element : conflictsMap.keySet()) {
|
||||
conflicts.put(element, conflictsMap.get(element));
|
||||
}
|
||||
if (myCurrentInheritor != null) {
|
||||
ReferencesSearch.search(myCurrentInheritor).forEach(new Processor<PsiReference>() {
|
||||
@Override
|
||||
public boolean process(PsiReference reference) {
|
||||
final PsiElement element = reference.getElement();
|
||||
if (element != null) {
|
||||
final PsiElement parent = element.getParent();
|
||||
if (parent instanceof PsiNewExpression) {
|
||||
final PsiClass aClass = PsiUtil.resolveClassInType(getPlaceExpectedType(parent));
|
||||
if (aClass == mySuperClass) {
|
||||
conflicts.putValue(parent, "Instance of target type is passed to a place where super class is expected.");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
});
|
||||
}
|
||||
checkConflicts(refUsages, conflicts);
|
||||
return showConflicts(conflicts, refUsages.get());
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static PsiType getPlaceExpectedType(PsiElement parent) {
|
||||
PsiType type = PsiTypesUtil.getExpectedTypeByParent((PsiExpression)parent);
|
||||
if (type == null) {
|
||||
final PsiElement arg = PsiUtil.skipParenthesizedExprUp(parent);
|
||||
final PsiElement gParent = arg.getParent();
|
||||
if (gParent instanceof PsiExpressionList) {
|
||||
int i = ArrayUtilRt.find(((PsiExpressionList)gParent).getExpressions(), arg);
|
||||
final PsiElement pParent = gParent.getParent();
|
||||
if (pParent instanceof PsiCallExpression) {
|
||||
final PsiMethod method = ((PsiCallExpression)pParent).resolveMethod();
|
||||
if (method != null) {
|
||||
final PsiParameter[] parameters = method.getParameterList().getParameters();
|
||||
if (i >= parameters.length) {
|
||||
if (method.isVarArgs()) {
|
||||
return ((PsiEllipsisType)parameters[parameters.length - 1].getType()).getComponentType();
|
||||
}
|
||||
} else {
|
||||
return parameters[i].getType();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return type;
|
||||
}
|
||||
|
||||
protected void performRefactoring(final UsageInfo[] usages) {
|
||||
new PushDownProcessor(mySuperClass.getProject(), myMemberInfos, mySuperClass, new DocCommentPolicy(myPolicy)) {
|
||||
//push down conflicts are already collected
|
||||
@@ -218,7 +284,12 @@ public class InlineSuperClassRefactoringProcessor extends FixableUsagesRefactori
|
||||
|
||||
@Override
|
||||
protected void performRefactoring(UsageInfo[] pushDownUsages) {
|
||||
super.performRefactoring(pushDownUsages);
|
||||
if (myCurrentInheritor != null) {
|
||||
encodeRefs();
|
||||
pushDownToClass(myCurrentInheritor);
|
||||
} else {
|
||||
super.performRefactoring(pushDownUsages);
|
||||
}
|
||||
RefactoringUtil.sortDepthFirstRightLeftOrder(usages);
|
||||
for (UsageInfo usageInfo : usages) {
|
||||
if (!(usageInfo instanceof ReplaceExtendsListUsageInfo || usageInfo instanceof RemoveImportUsageInfo)) {
|
||||
@@ -238,11 +309,13 @@ public class InlineSuperClassRefactoringProcessor extends FixableUsagesRefactori
|
||||
((FixableUsageInfo)usage).fixUsage();
|
||||
}
|
||||
}
|
||||
try {
|
||||
mySuperClass.delete();
|
||||
}
|
||||
catch (IncorrectOperationException e) {
|
||||
LOG.error(e);
|
||||
if (myCurrentInheritor == null) {
|
||||
try {
|
||||
mySuperClass.delete();
|
||||
}
|
||||
catch (IncorrectOperationException e) {
|
||||
LOG.error(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}.run();
|
||||
|
||||
@@ -161,7 +161,7 @@ public class PushDownProcessor extends BaseRefactoringProcessor {
|
||||
private static final Key<Boolean> REMOVE_QUALIFIER_KEY = Key.create("REMOVE_QUALIFIER_KEY");
|
||||
private static final Key<PsiClass> REPLACE_QUALIFIER_KEY = Key.create("REPLACE_QUALIFIER_KEY");
|
||||
|
||||
private void encodeRefs() {
|
||||
protected void encodeRefs() {
|
||||
final Set<PsiMember> movedMembers = new HashSet<PsiMember>();
|
||||
for (MemberInfo memberInfo : myMemberInfos) {
|
||||
movedMembers.add(memberInfo.getMember());
|
||||
@@ -328,7 +328,7 @@ public class PushDownProcessor extends BaseRefactoringProcessor {
|
||||
}
|
||||
}
|
||||
|
||||
private void pushDownToClass(PsiClass targetClass) throws IncorrectOperationException {
|
||||
protected void pushDownToClass(PsiClass targetClass) throws IncorrectOperationException {
|
||||
final PsiElementFactory factory = JavaPsiFacade.getInstance(myClass.getProject()).getElementFactory();
|
||||
final PsiSubstitutor substitutor = TypeConversionUtil.getSuperClassSubstitutor(myClass, targetClass, PsiSubstitutor.EMPTY);
|
||||
for (MemberInfo memberInfo : myMemberInfos) {
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
abstract class Super {
|
||||
public abstract void method() {}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
class Test {
|
||||
public void context() {
|
||||
method();
|
||||
}
|
||||
|
||||
public void method() {}
|
||||
}
|
||||
|
||||
class Test1 extends Super {
|
||||
@Override
|
||||
public void method() {}
|
||||
}
|
||||
|
||||
class U {
|
||||
Super t = new Test1();
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
abstract class Super {
|
||||
public abstract void method() {}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
class Test extends Super{
|
||||
public void context() {
|
||||
super.method();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void method() {}
|
||||
}
|
||||
|
||||
class Test1 extends Super {
|
||||
@Override
|
||||
public void method() {}
|
||||
}
|
||||
|
||||
class U {
|
||||
Super t = new Test1();
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
abstract class Super {
|
||||
public abstract void method() {}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
class Test {
|
||||
public void context() {
|
||||
method();
|
||||
}
|
||||
|
||||
public void method() {}
|
||||
}
|
||||
|
||||
class Test1 {
|
||||
public void method() {}
|
||||
}
|
||||
|
||||
class U {
|
||||
Test t = new Test1();
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
abstract class Super {
|
||||
public abstract void method() {}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
class Test extends Super{
|
||||
public void context() {
|
||||
super.method();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void method() {}
|
||||
}
|
||||
|
||||
class Test1 extends Super {
|
||||
@Override
|
||||
public void method() {}
|
||||
}
|
||||
|
||||
class U {
|
||||
Super t = new Test();
|
||||
}
|
||||
@@ -27,6 +27,10 @@ public class InlineSuperClassTest extends MultiFileTestCase {
|
||||
}
|
||||
|
||||
private void doTest(final boolean fail) throws Exception {
|
||||
doTest(fail, false);
|
||||
}
|
||||
|
||||
private void doTest(final boolean fail, final boolean inlineOne) throws Exception {
|
||||
try {
|
||||
doTest(new PerformAction() {
|
||||
@Override
|
||||
@@ -41,7 +45,7 @@ public class InlineSuperClassTest extends MultiFileTestCase {
|
||||
if (superClass == null) superClass = myJavaFacade.findClass("p1.Super", GlobalSearchScope.allScope(myProject));
|
||||
assertNotNull("Class Super not found", superClass);
|
||||
|
||||
new InlineSuperClassRefactoringProcessor(getProject(), superClass, DocCommentPolicy.ASIS, aClass).run();
|
||||
new InlineSuperClassRefactoringProcessor(getProject(), inlineOne ? aClass : null, superClass, DocCommentPolicy.ASIS, aClass).run();
|
||||
|
||||
//LocalFileSystem.getInstance().refresh(false);
|
||||
//FileDocumentManager.getInstance().saveAllDocuments();
|
||||
@@ -61,6 +65,14 @@ public class InlineSuperClassTest extends MultiFileTestCase {
|
||||
}
|
||||
}
|
||||
|
||||
public void testInlineOneClass() throws Exception {
|
||||
doTest(false, true);
|
||||
}
|
||||
|
||||
public void testInlineOneClassWithConflicts() throws Exception {
|
||||
doTest(true, true);
|
||||
}
|
||||
|
||||
public void testAbstractOverrides() throws Exception {
|
||||
doTest();
|
||||
}
|
||||
@@ -184,7 +196,7 @@ public class InlineSuperClassTest extends MultiFileTestCase {
|
||||
PsiClass superClass = myJavaFacade.findClass("Super", GlobalSearchScope.allScope(myProject));
|
||||
if (superClass == null) superClass = myJavaFacade.findClass("p1.Super", GlobalSearchScope.allScope(myProject));
|
||||
assertNotNull("Class Super not found", superClass);
|
||||
new InlineSuperClassRefactoringProcessor(getProject(), superClass, DocCommentPolicy.ASIS,
|
||||
new InlineSuperClassRefactoringProcessor(getProject(), null, superClass, DocCommentPolicy.ASIS,
|
||||
myJavaFacade.findClass("Test", GlobalSearchScope.allScope(myProject)),
|
||||
myJavaFacade.findClass("Test1", GlobalSearchScope.allScope(myProject))).run();
|
||||
}
|
||||
|
||||
@@ -426,6 +426,8 @@ inline.field.field.name.label=Field {0}
|
||||
inline.field.border.title=Inline
|
||||
all.references.and.remove.the.field=Inline &all references and remove the field
|
||||
this.reference.only.and.keep.the.field=Inline this reference only and &keep the field
|
||||
all.references.and.remove.super.class=Inline &all references and remove the class
|
||||
this.reference.only.and.keep.super.class=Inline this reference only and &keep the super class
|
||||
inline.variable.title=Inline Variable
|
||||
variable.is.referenced.in.multiple.files=Variable {0} is referenced in multiple files
|
||||
variable.is.never.used.before.modification=Variable {0} is never used before modification
|
||||
|
||||
Reference in New Issue
Block a user