mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
push down: add option to create a subclass if a class has no inheriters (IDEA-46563)
This commit is contained in:
+34
-25
@@ -37,7 +37,6 @@ import com.intellij.openapi.fileEditor.ex.IdeDocumentHistory;
|
||||
import com.intellij.openapi.module.ModuleUtil;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.ui.Messages;
|
||||
import com.intellij.openapi.util.Computable;
|
||||
import com.intellij.openapi.util.TextRange;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.psi.impl.source.PostprocessReformattingAspect;
|
||||
@@ -87,50 +86,61 @@ public class CreateSubclassAction extends PsiElementBaseIntentionAction {
|
||||
TextRange declarationRange = HighlightNamesUtil.getClassDeclarationTextRange(psiClass);
|
||||
if (!declarationRange.contains(element.getTextRange())) return false;
|
||||
|
||||
myText = psiClass.isInterface()
|
||||
myText = getTitle(psiClass);
|
||||
return true;
|
||||
}
|
||||
|
||||
private static String getTitle(PsiClass psiClass) {
|
||||
return psiClass.isInterface()
|
||||
? CodeInsightBundle.message("intention.implement.abstract.class.interface.text")
|
||||
: psiClass.hasModifierProperty(PsiModifier.ABSTRACT)
|
||||
? CodeInsightBundle.message("intention.implement.abstract.class.default.text")
|
||||
: CodeInsightBundle.message("intention.implement.abstract.class.subclass.text");
|
||||
return true;
|
||||
}
|
||||
|
||||
public void invoke(@NotNull final Project project, Editor editor, final PsiFile file) throws IncorrectOperationException {
|
||||
PsiElement element = file.findElementAt(editor.getCaretModel().getOffset());
|
||||
final PsiClass psiClass = PsiTreeUtil.getParentOfType(element, PsiClass.class);
|
||||
|
||||
PsiDirectory sourceDir = file.getContainingDirectory();
|
||||
final CreateClassDialog dlg = chooseSubclassToCreate(psiClass);
|
||||
if (dlg != null) {
|
||||
createSubclass(psiClass, dlg.getTargetDirectory(), dlg.getClassName());
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static CreateClassDialog chooseSubclassToCreate(PsiClass psiClass) {
|
||||
PsiDirectory sourceDir = psiClass.getContainingFile().getContainingDirectory();
|
||||
|
||||
final PsiPackage aPackage = JavaDirectoryService.getInstance().getPackage(sourceDir);
|
||||
final CreateClassDialog dialog = new CreateClassDialog(
|
||||
project,
|
||||
myText,
|
||||
psiClass.getProject(), getTitle(psiClass),
|
||||
psiClass.getName() + IMPL_SUFFIX,
|
||||
aPackage != null ? aPackage.getQualifiedName() : "",
|
||||
CreateClassKind.CLASS, true, ModuleUtil.findModuleForPsiElement(file));
|
||||
CreateClassKind.CLASS, true, ModuleUtil.findModuleForPsiElement(psiClass));
|
||||
dialog.show();
|
||||
if (!dialog.isOK()) return;
|
||||
if (!dialog.isOK()) return null;
|
||||
final PsiDirectory targetDirectory = dialog.getTargetDirectory();
|
||||
if (targetDirectory == null) return;
|
||||
|
||||
createSubclass(psiClass, targetDirectory, dialog.getClassName());
|
||||
if (targetDirectory == null) return null;
|
||||
return dialog;
|
||||
}
|
||||
|
||||
public static void createSubclass(final PsiClass psiClass, final PsiDirectory targetDirectory, final String className) {
|
||||
public static PsiClass createSubclass(final PsiClass psiClass, final PsiDirectory targetDirectory, final String className) {
|
||||
final Project project = psiClass.getProject();
|
||||
final PsiClass[] targetClass = new PsiClass[1];
|
||||
PostprocessReformattingAspect.getInstance(project).postponeFormattingInside(new Runnable () {
|
||||
public void run() {
|
||||
PsiClass targetClass = ApplicationManager.getApplication().runWriteAction(new Computable<PsiClass>() {
|
||||
public PsiClass compute() {
|
||||
ApplicationManager.getApplication().runWriteAction(new Runnable() {
|
||||
public void run() {
|
||||
|
||||
IdeDocumentHistory.getInstance(project).includeCurrentPlaceAsChangePlace();
|
||||
|
||||
final PsiTypeParameterList oldTypeParameterList = psiClass.getTypeParameterList();
|
||||
PsiClass targetClass;
|
||||
|
||||
try {
|
||||
targetClass = JavaDirectoryService.getInstance().createClass(targetDirectory, className);
|
||||
targetClass[0] = JavaDirectoryService.getInstance().createClass(targetDirectory, className);
|
||||
if (psiClass.hasTypeParameters()) {
|
||||
final PsiTypeParameterList typeParameterList = targetClass.getTypeParameterList();
|
||||
final PsiTypeParameterList typeParameterList = targetClass[0].getTypeParameterList();
|
||||
assert typeParameterList != null;
|
||||
typeParameterList.replace(oldTypeParameterList);
|
||||
}
|
||||
@@ -143,16 +153,16 @@ public class CreateSubclassAction extends PsiElementBaseIntentionAction {
|
||||
CodeInsightBundle.message("intention.error.cannot.create.class.title"));
|
||||
}
|
||||
});
|
||||
return null;
|
||||
return;
|
||||
}
|
||||
final PsiElementFactory elementFactory = JavaPsiFacade.getInstance(project).getElementFactory();
|
||||
PsiJavaCodeReferenceElement ref = elementFactory.createClassReferenceElement(psiClass);
|
||||
try {
|
||||
if (psiClass.isInterface()) {
|
||||
ref = (PsiJavaCodeReferenceElement)targetClass.getImplementsList().add(ref);
|
||||
ref = (PsiJavaCodeReferenceElement)targetClass[0].getImplementsList().add(ref);
|
||||
}
|
||||
else {
|
||||
ref = (PsiJavaCodeReferenceElement)targetClass.getExtendsList().add(ref);
|
||||
ref = (PsiJavaCodeReferenceElement)targetClass[0].getExtendsList().add(ref);
|
||||
}
|
||||
|
||||
if (oldTypeParameterList != null) {
|
||||
@@ -164,19 +174,18 @@ public class CreateSubclassAction extends PsiElementBaseIntentionAction {
|
||||
catch (IncorrectOperationException e) {
|
||||
LOG.error(e);
|
||||
}
|
||||
|
||||
return targetClass;
|
||||
}
|
||||
});
|
||||
if (targetClass == null) return;
|
||||
if (targetClass[0] == null) return;
|
||||
|
||||
if (!ApplicationManager.getApplication().isUnitTestMode()) {
|
||||
final Editor editor1 = CodeInsightUtil.positionCursor(project, targetClass.getContainingFile(), targetClass.getLBrace());
|
||||
final Editor editor1 = CodeInsightUtil.positionCursor(project, targetClass[0].getContainingFile(), targetClass[0].getLBrace());
|
||||
if (editor1 == null) return;
|
||||
OverrideImplementUtil.chooseAndImplementMethods(project, editor1, targetClass);
|
||||
OverrideImplementUtil.chooseAndImplementMethods(project, editor1, targetClass[0]);
|
||||
}
|
||||
}
|
||||
});
|
||||
return targetClass[0];
|
||||
}
|
||||
|
||||
public boolean startInWriteAction() {
|
||||
|
||||
@@ -32,7 +32,7 @@ import java.util.Set;
|
||||
public class PushDownConflicts {
|
||||
private final PsiClass myClass;
|
||||
private final Set<PsiMember> myMovedMembers;
|
||||
private final Set<PsiMember> myAbstractMembers;
|
||||
private final Set<PsiMethod> myAbstractMembers;
|
||||
private final MultiMap<PsiElement, String> myConflicts;
|
||||
|
||||
|
||||
@@ -40,13 +40,13 @@ public class PushDownConflicts {
|
||||
myClass = aClass;
|
||||
|
||||
myMovedMembers = new HashSet<PsiMember>();
|
||||
myAbstractMembers = new HashSet<PsiMember>();
|
||||
myAbstractMembers = new HashSet<PsiMethod>();
|
||||
for (MemberInfo memberInfo : memberInfos) {
|
||||
final PsiMember member = memberInfo.getMember();
|
||||
if (memberInfo.isChecked() && (!(memberInfo.getMember() instanceof PsiClass) || memberInfo.getOverrides() == null)) {
|
||||
myMovedMembers.add(member);
|
||||
if (memberInfo.isToAbstract()) {
|
||||
myAbstractMembers.add(member);
|
||||
myAbstractMembers.add((PsiMethod)member);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -71,9 +71,11 @@ public class PushDownConflicts {
|
||||
}
|
||||
}
|
||||
|
||||
public void checkTargetClassConflicts(PsiClass targetClass, boolean checkStatic) {
|
||||
for (final PsiMember movedMember : myMovedMembers) {
|
||||
checkMemberPlacementInTargetClassConflict(targetClass, movedMember);
|
||||
public void checkTargetClassConflicts(PsiClass targetClass, boolean checkStatic, PsiElement context) {
|
||||
if (targetClass != null) {
|
||||
for (final PsiMember movedMember : myMovedMembers) {
|
||||
checkMemberPlacementInTargetClassConflict(targetClass, movedMember);
|
||||
}
|
||||
}
|
||||
Members:
|
||||
for (PsiMember member : myMovedMembers) {
|
||||
@@ -106,7 +108,7 @@ public class PushDownConflicts {
|
||||
}
|
||||
}
|
||||
}
|
||||
RefactoringConflictsUtil.analyzeAccessibilityConflicts(myMovedMembers, targetClass, myConflicts, null);
|
||||
RefactoringConflictsUtil.analyzeAccessibilityConflicts(myMovedMembers, targetClass, myConflicts, null, context, myAbstractMembers);
|
||||
}
|
||||
|
||||
public void checkMemberPlacementInTargetClassConflict(final PsiClass targetClass, final PsiMember movedMember) {
|
||||
|
||||
@@ -17,8 +17,11 @@ package com.intellij.refactoring.memberPushDown;
|
||||
|
||||
import com.intellij.codeInsight.AnnotationUtil;
|
||||
import com.intellij.codeInsight.ChangeContextUtil;
|
||||
import com.intellij.codeInsight.intention.impl.CreateClassDialog;
|
||||
import com.intellij.codeInsight.intention.impl.CreateSubclassAction;
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.ui.DialogWrapper;
|
||||
import com.intellij.openapi.ui.Messages;
|
||||
import com.intellij.openapi.util.Key;
|
||||
import com.intellij.openapi.util.Ref;
|
||||
@@ -49,6 +52,7 @@ public class PushDownProcessor extends BaseRefactoringProcessor {
|
||||
private final MemberInfo[] myMemberInfos;
|
||||
private PsiClass myClass;
|
||||
private final DocCommentPolicy myJavaDocPolicy;
|
||||
private CreateClassDialog myCreateClassDlg;
|
||||
|
||||
public PushDownProcessor(Project project,
|
||||
MemberInfo[] memberInfos,
|
||||
@@ -78,7 +82,7 @@ public class PushDownProcessor extends BaseRefactoringProcessor {
|
||||
return usages;
|
||||
}
|
||||
|
||||
protected boolean preprocessUsages(Ref<UsageInfo[]> refUsages) {
|
||||
protected boolean preprocessUsages(final Ref<UsageInfo[]> refUsages) {
|
||||
UsageInfo[] usagesIn = refUsages.get();
|
||||
final PushDownConflicts pushDownConflicts = new PushDownConflicts(myClass, myMemberInfos);
|
||||
pushDownConflicts.checkSourceClassConflicts();
|
||||
@@ -88,13 +92,21 @@ public class PushDownProcessor extends BaseRefactoringProcessor {
|
||||
RefactoringBundle.message("interface.0.does.not.have.inheritors", myClass.getQualifiedName()) :
|
||||
RefactoringBundle.message("class.0.does.not.have.inheritors", myClass.getQualifiedName());
|
||||
final String message = noInheritors + "\n" + RefactoringBundle.message("push.down.will.delete.members");
|
||||
final int answer = Messages.showYesNoDialog(message, JavaPushDownHandler.REFACTORING_NAME, Messages.getWarningIcon());
|
||||
if (answer != 0) return false;
|
||||
final int answer = Messages.showYesNoCancelDialog(message, JavaPushDownHandler.REFACTORING_NAME, Messages.getWarningIcon());
|
||||
if (answer == DialogWrapper.OK_EXIT_CODE) {
|
||||
myCreateClassDlg = CreateSubclassAction.chooseSubclassToCreate(myClass);
|
||||
if (myCreateClassDlg != null) {
|
||||
pushDownConflicts.checkTargetClassConflicts(null, false, myCreateClassDlg.getTargetDirectory());
|
||||
return showConflicts(pushDownConflicts.getConflicts());
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
} else if (answer != 1) return false;
|
||||
}
|
||||
for (UsageInfo usage : usagesIn) {
|
||||
final PsiElement element = usage.getElement();
|
||||
if (element instanceof PsiClass) {
|
||||
pushDownConflicts.checkTargetClassConflicts((PsiClass)element, usagesIn.length > 1);
|
||||
pushDownConflicts.checkTargetClassConflicts((PsiClass)element, usagesIn.length > 1, element);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -113,6 +125,13 @@ public class PushDownProcessor extends BaseRefactoringProcessor {
|
||||
protected void performRefactoring(UsageInfo[] usages) {
|
||||
try {
|
||||
encodeRefs();
|
||||
if (myCreateClassDlg != null) { //usages.length == 0
|
||||
final PsiClass psiClass =
|
||||
CreateSubclassAction.createSubclass(myClass, myCreateClassDlg.getTargetDirectory(), myCreateClassDlg.getClassName());
|
||||
if (psiClass != null) {
|
||||
pushDownToClass(psiClass);
|
||||
}
|
||||
}
|
||||
for (UsageInfo usage : usages) {
|
||||
if (usage.getElement() instanceof PsiClass) {
|
||||
final PsiClass targetClass = (PsiClass)usage.getElement();
|
||||
|
||||
@@ -472,7 +472,7 @@ push.down.javadoc.panel.title=JavaDoc for abstracts
|
||||
push.down.members.elements.header=Push down members from
|
||||
interface.0.does.not.have.inheritors=Interface {0} does not have inheritors.
|
||||
class.0.does.not.have.inheritors=Class {0} does not have inheritors.
|
||||
push.down.will.delete.members=Pushing members down will result in them being deleted. Continue?
|
||||
push.down.will.delete.members=Pushing members down will result in them being deleted. Would you like to create a new subclass?
|
||||
edit.migration.map.title=Edit Migration Map
|
||||
migration.map.name.prompt=Map name:
|
||||
migration.map.description.label=Map description:
|
||||
|
||||
Reference in New Issue
Block a user