diff --git a/java/java-impl/src/com/intellij/codeInsight/intention/impl/CreateSubclassAction.java b/java/java-impl/src/com/intellij/codeInsight/intention/impl/CreateSubclassAction.java index 6de444d8e6d3..a504b6a73602 100644 --- a/java/java-impl/src/com/intellij/codeInsight/intention/impl/CreateSubclassAction.java +++ b/java/java-impl/src/com/intellij/codeInsight/intention/impl/CreateSubclassAction.java @@ -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() { - 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() { diff --git a/java/java-impl/src/com/intellij/refactoring/memberPushDown/PushDownConflicts.java b/java/java-impl/src/com/intellij/refactoring/memberPushDown/PushDownConflicts.java index e0dcbe78bc33..ffa72b19c42c 100644 --- a/java/java-impl/src/com/intellij/refactoring/memberPushDown/PushDownConflicts.java +++ b/java/java-impl/src/com/intellij/refactoring/memberPushDown/PushDownConflicts.java @@ -32,7 +32,7 @@ import java.util.Set; public class PushDownConflicts { private final PsiClass myClass; private final Set myMovedMembers; - private final Set myAbstractMembers; + private final Set myAbstractMembers; private final MultiMap myConflicts; @@ -40,13 +40,13 @@ public class PushDownConflicts { myClass = aClass; myMovedMembers = new HashSet(); - myAbstractMembers = new HashSet(); + myAbstractMembers = new HashSet(); 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) { diff --git a/java/java-impl/src/com/intellij/refactoring/memberPushDown/PushDownProcessor.java b/java/java-impl/src/com/intellij/refactoring/memberPushDown/PushDownProcessor.java index 2bae832f3cf9..111df6d87abc 100644 --- a/java/java-impl/src/com/intellij/refactoring/memberPushDown/PushDownProcessor.java +++ b/java/java-impl/src/com/intellij/refactoring/memberPushDown/PushDownProcessor.java @@ -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 refUsages) { + protected boolean preprocessUsages(final Ref 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(); diff --git a/platform/platform-resources-en/src/messages/RefactoringBundle.properties b/platform/platform-resources-en/src/messages/RefactoringBundle.properties index 64254c508fa9..35a6dc0eded0 100644 --- a/platform/platform-resources-en/src/messages/RefactoringBundle.properties +++ b/platform/platform-resources-en/src/messages/RefactoringBundle.properties @@ -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: