extensibility for Pull Up and Push Down
[fedora-idea.git] / refactoring / impl / com / intellij / refactoring / memberPullUp / JavaPullUpHandler.java
blobbf5b4725d238fdf435ffffc6cf7fa62a6ee47a06
1 /*
2 * Created by IntelliJ IDEA.
3 * User: dsl
4 * Date: 18.06.2002
5 * Time: 12:45:30
6 * To change template for new class use
7 * Code Style | Class Templates options (Tools | IDE Options).
8 */
9 package com.intellij.refactoring.memberPullUp;
11 import com.intellij.history.LocalHistoryAction;
12 import com.intellij.history.LocalHistory;
13 import com.intellij.openapi.actionSystem.DataContext;
14 import com.intellij.openapi.actionSystem.PlatformDataKeys;
15 import com.intellij.openapi.application.ApplicationManager;
16 import com.intellij.openapi.command.CommandProcessor;
17 import com.intellij.openapi.diagnostic.Logger;
18 import com.intellij.openapi.editor.Editor;
19 import com.intellij.openapi.editor.ScrollType;
20 import com.intellij.openapi.project.Project;
21 import com.intellij.psi.*;
22 import com.intellij.refactoring.HelpID;
23 import com.intellij.refactoring.RefactoringActionHandler;
24 import com.intellij.refactoring.RefactoringBundle;
25 import com.intellij.refactoring.lang.ElementsHandler;
26 import com.intellij.refactoring.ui.ConflictsDialog;
27 import com.intellij.refactoring.util.CommonRefactoringUtil;
28 import com.intellij.refactoring.util.JavaDocPolicy;
29 import com.intellij.refactoring.util.RefactoringHierarchyUtil;
30 import com.intellij.refactoring.util.classMembers.MemberInfo;
31 import com.intellij.refactoring.util.classMembers.MemberInfoStorage;
32 import com.intellij.usageView.UsageViewUtil;
33 import com.intellij.util.IncorrectOperationException;
34 import org.jetbrains.annotations.NotNull;
36 import java.util.ArrayList;
37 import java.util.List;
39 public class JavaPullUpHandler implements RefactoringActionHandler, PullUpDialog.Callback, ElementsHandler {
40 private static final Logger LOG = Logger.getInstance("#com.intellij.refactoring.memberPullUp.JavaPullUpHandler");
41 public static final String REFACTORING_NAME = RefactoringBundle.message("pull.members.up.title");
42 private PsiClass mySubclass;
43 private Project myProject;
45 public void invoke(@NotNull Project project, Editor editor, PsiFile file, DataContext dataContext) {
46 int offset = editor.getCaretModel().getOffset();
47 editor.getScrollingModel().scrollToCaret(ScrollType.MAKE_VISIBLE);
48 PsiElement element = file.findElementAt(offset);
50 while (true) {
51 if (element == null || element instanceof PsiFile) {
52 String message = RefactoringBundle
53 .getCannotRefactorMessage(RefactoringBundle.message("the.caret.should.be.positioned.inside.a.class.to.pull.members.from"));
54 CommonRefactoringUtil.showErrorHint(project, editor, message, REFACTORING_NAME, HelpID.MEMBERS_PULL_UP);
55 return;
58 if (!CommonRefactoringUtil.checkReadOnlyStatus(project, element)) return;
60 if (element instanceof PsiClass || element instanceof PsiField || element instanceof PsiMethod) {
61 invoke(project, new PsiElement[]{element}, dataContext);
62 return;
64 element = element.getParent();
68 public void invoke(@NotNull final Project project, @NotNull PsiElement[] elements, DataContext dataContext) {
69 if (elements.length != 1) return;
70 myProject = project;
72 PsiElement element = elements[0];
73 PsiClass aClass;
74 PsiElement aMember = null;
76 if (element instanceof PsiClass) {
77 aClass = (PsiClass)element;
79 else if (element instanceof PsiMethod) {
80 aClass = ((PsiMethod)element).getContainingClass();
81 aMember = element;
83 else if (element instanceof PsiField) {
84 aClass = ((PsiField)element).getContainingClass();
85 aMember = element;
87 else {
88 return;
91 Editor editor = PlatformDataKeys.EDITOR.getData(dataContext);
92 if (aClass == null) {
93 String message =
94 RefactoringBundle.getCannotRefactorMessage(RefactoringBundle.message("is.not.supported.in.the.current.context", REFACTORING_NAME));
95 CommonRefactoringUtil.showErrorHint(project, editor, message, REFACTORING_NAME, HelpID.MEMBERS_PULL_UP);
96 return;
99 ArrayList<PsiClass> bases = RefactoringHierarchyUtil.createBasesList(aClass, false, true);
101 if (bases.isEmpty()) {
102 String message = RefactoringBundle.getCannotRefactorMessage(
103 RefactoringBundle.message("class.does.not.have.base.classes.interfaces.in.current.project", aClass.getQualifiedName()));
104 CommonRefactoringUtil.showErrorHint(project, editor, message, REFACTORING_NAME, HelpID.MEMBERS_PULL_UP);
105 return;
109 mySubclass = aClass;
110 MemberInfoStorage memberInfoStorage = new MemberInfoStorage(mySubclass, new MemberInfo.Filter() {
111 public boolean includeMember(PsiMember element) {
112 return true;
115 List<MemberInfo> members = memberInfoStorage.getClassMemberInfos(mySubclass);
116 PsiManager manager = mySubclass.getManager();
118 for (MemberInfo member : members) {
119 if (manager.areElementsEquivalent(member.getMember(), aMember)) {
120 member.setChecked(true);
121 break;
125 final PullUpDialog dialog = new PullUpDialog(project, aClass, bases, memberInfoStorage, this);
128 dialog.show();
130 if (!dialog.isOK()) return;
132 CommandProcessor.getInstance().executeCommand(myProject, new Runnable() {
133 public void run() {
134 final Runnable action = new Runnable() {
135 public void run() {
136 doRefactoring(dialog);
139 ApplicationManager.getApplication().runWriteAction(action);
141 }, REFACTORING_NAME, null);
146 private void doRefactoring(PullUpDialog dialog) {
147 LocalHistoryAction a = LocalHistory.startAction(myProject, getCommandName());
148 try {
149 try {
150 PullUpHelper helper = new PullUpHelper(mySubclass, dialog.getSuperClass(), dialog.getSelectedMemberInfos(),
151 new JavaDocPolicy(dialog.getJavaDocPolicy()));
152 helper.moveMembersToBase();
153 helper.moveFieldInitializations();
155 finally {
156 a.finish();
159 catch (IncorrectOperationException e) {
160 LOG.error(e);
164 private String getCommandName() {
165 return RefactoringBundle.message("pullUp.command", UsageViewUtil.getDescriptiveName(mySubclass));
168 public boolean checkConflicts(PullUpDialog dialog) {
169 final MemberInfo[] infos = dialog.getSelectedMemberInfos();
170 PsiClass superClass = dialog.getSuperClass();
171 if (!checkWritable(superClass, infos)) return false;
172 String[] conflicts = PullUpConflictsUtil.checkConflicts(infos, mySubclass, superClass, null, null, dialog.getContainmentVerifier());
173 if (conflicts.length > 0) {
174 ConflictsDialog conflictsDialog = new ConflictsDialog(myProject, conflicts);
175 conflictsDialog.show();
176 return conflictsDialog.isOK();
178 return true;
181 private boolean checkWritable(PsiClass superClass, MemberInfo[] infos) {
182 if (!CommonRefactoringUtil.checkReadOnlyStatus(myProject, superClass)) return false;
183 for (MemberInfo info : infos) {
184 if (info.getMember() instanceof PsiClass && info.getOverrides() != null) continue;
185 if (!CommonRefactoringUtil.checkReadOnlyStatus(myProject, info.getMember())) return false;
187 return true;
190 public boolean isEnabledOnElements(PsiElement[] elements) {
192 if (elements.length == 1) {
193 return elements[0] instanceof PsiClass || elements[0] instanceof PsiField || elements[0] instanceof PsiMethod;
195 else if (elements.length > 1){
196 for (int idx = 0; idx < elements.length; idx++) {
197 PsiElement element = elements[idx];
198 if (!(element instanceof PsiField || element instanceof PsiMethod)) return false;
200 return true;
202 return false;
204 // todo: multiple selection etc
205 return elements.length == 1 && elements[0] instanceof PsiClass;