class members refactoring refactoring
[fedora-idea.git] / refactoring / impl / com / intellij / refactoring / move / moveMembers / MoveMembersDialog.java
blob85b98a778f827106dd61275478f673af566b7fac
1 package com.intellij.refactoring.move.moveMembers;
3 import com.intellij.ide.util.PackageUtil;
4 import com.intellij.ide.util.TreeClassChooser;
5 import com.intellij.ide.util.TreeClassChooserFactory;
6 import com.intellij.openapi.application.ApplicationManager;
7 import com.intellij.openapi.command.CommandProcessor;
8 import com.intellij.openapi.help.HelpManager;
9 import com.intellij.openapi.options.ConfigurationException;
10 import com.intellij.openapi.project.Project;
11 import com.intellij.openapi.ui.Messages;
12 import com.intellij.openapi.util.Computable;
13 import com.intellij.openapi.util.Ref;
14 import com.intellij.psi.*;
15 import com.intellij.psi.search.GlobalSearchScope;
16 import com.intellij.psi.util.PsiTreeUtil;
17 import com.intellij.refactoring.HelpID;
18 import com.intellij.refactoring.JavaRefactoringSettings;
19 import com.intellij.refactoring.RefactoringBundle;
20 import com.intellij.refactoring.classMembers.MemberInfoChange;
21 import com.intellij.refactoring.move.MoveCallback;
22 import com.intellij.refactoring.ui.MemberSelectionTable;
23 import com.intellij.refactoring.ui.RefactoringDialog;
24 import com.intellij.refactoring.ui.VisibilityPanel;
25 import com.intellij.refactoring.util.CommonRefactoringUtil;
26 import com.intellij.refactoring.util.classMembers.MemberInfo;
27 import com.intellij.refactoring.util.classMembers.UsesAndInterfacesDependencyMemberInfoModel;
28 import com.intellij.ui.IdeBorderFactory;
29 import com.intellij.ui.RecentsManager;
30 import com.intellij.ui.ReferenceEditorComboWithBrowseButton;
31 import com.intellij.ui.ScrollPaneFactory;
32 import com.intellij.usageView.UsageViewUtil;
33 import com.intellij.util.IncorrectOperationException;
34 import org.jetbrains.annotations.NonNls;
36 import javax.swing.*;
37 import javax.swing.border.Border;
38 import java.awt.*;
39 import java.awt.event.ActionEvent;
40 import java.awt.event.ActionListener;
41 import java.util.ArrayList;
42 import java.util.Collection;
43 import java.util.List;
44 import java.util.Set;
46 public class MoveMembersDialog extends RefactoringDialog implements MoveMembersOptions {
47 @NonNls private static final String RECENTS_KEY = "MoveMembersDialog.RECENTS_KEY";
48 private MyMemberInfoModel myMemberInfoModel;
50 private final Project myProject;
51 private final PsiClass mySourceClass;
52 private final String mySourceClassName;
53 private final List<MemberInfo> myMemberInfos;
54 private final ReferenceEditorComboWithBrowseButton myTfTargetClassName;
55 private MemberSelectionTable myTable;
56 private final MoveCallback myMoveCallback;
58 VisibilityPanel myVisibilityPanel;
59 private final JCheckBox myIntroduceEnumConstants = new JCheckBox(RefactoringBundle.message("move.enum.constant.cb"), true);
61 public MoveMembersDialog(Project project,
62 PsiClass sourceClass,
63 final PsiClass initialTargetClass,
64 Set<PsiMember> preselectMembers,
65 MoveCallback moveCallback) {
66 super(project, true);
67 myProject = project;
68 mySourceClass = sourceClass;
69 myMoveCallback = moveCallback;
70 setTitle(MoveMembersImpl.REFACTORING_NAME);
72 mySourceClassName = mySourceClass.getQualifiedName();
74 PsiField[] fields = mySourceClass.getFields();
75 PsiMethod[] methods = mySourceClass.getMethods();
76 PsiClass[] innerClasses = mySourceClass.getInnerClasses();
77 ArrayList<MemberInfo> memberList = new ArrayList<MemberInfo>(fields.length + methods.length);
79 for (PsiClass innerClass : innerClasses) {
80 if (!innerClass.hasModifierProperty(PsiModifier.STATIC)) continue;
81 MemberInfo info = new MemberInfo(innerClass);
82 if (preselectMembers.contains(innerClass)) {
83 info.setChecked(true);
85 memberList.add(info);
87 boolean hasConstantFields = false;
88 for (PsiField field : fields) {
89 if (field.hasModifierProperty(PsiModifier.STATIC)) {
90 MemberInfo info = new MemberInfo(field);
91 if (preselectMembers.contains(field)) {
92 info.setChecked(true);
94 memberList.add(info);
95 hasConstantFields = true;
98 if (!hasConstantFields) myIntroduceEnumConstants.setVisible(false);
99 for (PsiMethod method : methods) {
100 if (method.hasModifierProperty(PsiModifier.STATIC)) {
101 MemberInfo info = new MemberInfo(method);
102 if (preselectMembers.contains(method)) {
103 info.setChecked(true);
105 memberList.add(info);
108 myMemberInfos = memberList;
109 String fqName = initialTargetClass != null && !sourceClass.equals(initialTargetClass) ? initialTargetClass.getQualifiedName() : "";
110 myTfTargetClassName = new ReferenceEditorComboWithBrowseButton(new ChooseClassAction(), fqName, PsiManager.getInstance(myProject), true, RECENTS_KEY);
112 init();
115 public String getMemberVisibility() {
116 return myVisibilityPanel.getVisibility();
119 public boolean makeEnumConstant() {
120 return myIntroduceEnumConstants.isVisible() && myIntroduceEnumConstants.isEnabled() && myIntroduceEnumConstants.isSelected();
123 protected String getDimensionServiceKey() {
124 return "#com.intellij.refactoring.move.moveMembers.MoveMembersDialog";
127 private JTable createTable() {
128 myMemberInfoModel = new MyMemberInfoModel();
129 myTable = new MemberSelectionTable(myMemberInfos, null);
130 myTable.setMemberInfoModel(myMemberInfoModel);
131 myTable.addMemberInfoChangeListener(myMemberInfoModel);
132 myMemberInfoModel.memberInfoChanged(new MemberInfoChange<PsiMember, MemberInfo>(myMemberInfos));
133 return myTable;
136 protected JComponent createNorthPanel() {
137 JPanel panel = new JPanel(new BorderLayout());
139 JPanel _panel;
140 Box box = Box.createVerticalBox();
142 _panel = new JPanel(new BorderLayout());
143 JTextField sourceClassField = new JTextField();
144 sourceClassField.setText(mySourceClassName);
145 sourceClassField.setEditable(false);
146 _panel.add(new JLabel(RefactoringBundle.message("move.members.move.members.from.label")), BorderLayout.NORTH);
147 _panel.add(sourceClassField, BorderLayout.CENTER);
148 box.add(_panel);
150 box.add(Box.createVerticalStrut(10));
152 _panel = new JPanel(new BorderLayout());
153 JLabel label = new JLabel(RefactoringBundle.message("move.members.to.fully.qualified.name.label"));
154 label.setLabelFor(myTfTargetClassName);
155 _panel.add(label, BorderLayout.NORTH);
156 _panel.add(myTfTargetClassName, BorderLayout.CENTER);
157 _panel.add(myIntroduceEnumConstants, BorderLayout.SOUTH);
158 box.add(_panel);
160 myTfTargetClassName.getChildComponent().getDocument().addDocumentListener(new com.intellij.openapi.editor.event.DocumentAdapter() {
161 public void documentChanged(com.intellij.openapi.editor.event.DocumentEvent e) {
162 myMemberInfoModel.updateTargetClass();
163 validateButtons();
167 panel.add(box, BorderLayout.CENTER);
168 panel.add(Box.createVerticalStrut(10), BorderLayout.SOUTH);
170 validateButtons();
171 return panel;
174 protected JComponent createCenterPanel() {
175 JPanel panel = new JPanel(new BorderLayout());
176 JTable table = createTable();
177 if (table.getRowCount() > 0) {
178 table.getSelectionModel().addSelectionInterval(0, 0);
180 JScrollPane scrollPane = ScrollPaneFactory.createScrollPane(table);
181 Border titledBorder = IdeBorderFactory.createTitledBorder(RefactoringBundle.message("move.members.members.to.be.moved.border.title"));
182 Border emptyBorder = BorderFactory.createEmptyBorder(0, 5, 5, 5);
183 Border border = BorderFactory.createCompoundBorder(titledBorder, emptyBorder);
184 scrollPane.setBorder(border);
185 panel.add(scrollPane, BorderLayout.CENTER);
187 myVisibilityPanel = new VisibilityPanel(true, true);
188 myVisibilityPanel.setVisibility(null);
189 panel.add(myVisibilityPanel, BorderLayout.EAST);
191 return panel;
194 public JComponent getPreferredFocusedComponent() {
195 return myTfTargetClassName.getChildComponent();
198 public PsiMember[] getSelectedMembers() {
199 final Collection<MemberInfo> selectedMemberInfos = myTable.getSelectedMemberInfos();
200 ArrayList<PsiMember> list = new ArrayList<PsiMember>();
201 for (MemberInfo selectedMemberInfo : selectedMemberInfos) {
202 list.add(selectedMemberInfo.getMember());
204 return list.toArray(new PsiMember[list.size()]);
207 public String getTargetClassName() {
208 return myTfTargetClassName.getText();
211 protected void doAction() {
212 String message = validateInputData();
214 if (message != null) {
215 if (message.length() != 0) {
216 CommonRefactoringUtil.showErrorMessage(
217 MoveMembersImpl.REFACTORING_NAME,
218 message,
219 HelpID.MOVE_MEMBERS,
220 myProject);
222 return;
225 invokeRefactoring(new MoveMembersProcessor(getProject(), myMoveCallback, new MoveMembersOptions() {
226 public String getMemberVisibility() {
227 return MoveMembersDialog.this.getMemberVisibility();
230 public boolean makeEnumConstant() {
231 return MoveMembersDialog.this.makeEnumConstant();
234 public PsiMember[] getSelectedMembers() {
235 return MoveMembersDialog.this.getSelectedMembers();
238 public String getTargetClassName() {
239 return MoveMembersDialog.this.getTargetClassName();
241 }));
243 JavaRefactoringSettings.getInstance().MOVE_PREVIEW_USAGES = isPreviewUsages();
246 @Override
247 protected void canRun() throws ConfigurationException {
248 if (getTargetClassName().length() == 0) throw new ConfigurationException("Destination class name not found");
251 private String validateInputData() {
252 final PsiManager manager = PsiManager.getInstance(myProject);
253 final String fqName = getTargetClassName();
254 if ("".equals(fqName)) {
255 return RefactoringBundle.message("no.destination.class.specified");
257 else {
258 if (!JavaPsiFacade.getInstance(manager.getProject()).getNameHelper().isQualifiedName(fqName)) {
259 return RefactoringBundle.message("0.is.not.a.legal.fq.name", fqName);
261 else {
262 RecentsManager.getInstance(myProject).registerRecentEntry(RECENTS_KEY, fqName);
263 final PsiClass[] targetClass = new PsiClass[]{null};
264 CommandProcessor.getInstance().executeCommand(myProject, new Runnable() {
265 public void run() {
266 try {
267 targetClass[0] = findOrCreateTargetClass(manager, fqName);
269 catch (IncorrectOperationException e) {
270 CommonRefactoringUtil.showErrorMessage(
271 MoveMembersImpl.REFACTORING_NAME,
272 e.getMessage(),
273 HelpID.MOVE_MEMBERS,
274 myProject);
278 }, RefactoringBundle.message("create.class.command", fqName), null);
280 if (targetClass[0] == null) {
281 return "";
284 if (mySourceClass.equals(targetClass[0])) {
285 return RefactoringBundle.message("source.and.destination.classes.should.be.different");
286 } else if (!mySourceClass.getLanguage().equals(targetClass[0].getLanguage())) {
287 return RefactoringBundle.message("move.to.different.language", UsageViewUtil.getType(mySourceClass), mySourceClass.getQualifiedName(), targetClass[0].getQualifiedName());
289 else {
290 for (MemberInfo info : myMemberInfos) {
291 if (!info.isChecked()) continue;
292 if (PsiTreeUtil.isAncestor(info.getMember(), targetClass[0], false)) {
293 return RefactoringBundle.message("cannot.move.inner.class.0.into.itself", info.getDisplayName());
297 if (!targetClass[0].isWritable()) {
298 if (!CommonRefactoringUtil.checkReadOnlyStatus(myProject, targetClass[0])) return "";
299 return "";
302 return null;
308 private PsiClass findOrCreateTargetClass(final PsiManager manager, final String fqName) throws IncorrectOperationException {
309 final String className;
310 final String packageName;
311 int dotIndex = fqName.lastIndexOf('.');
312 if (dotIndex >= 0) {
313 packageName = fqName.substring(0, dotIndex);
314 className = (dotIndex + 1 < fqName.length())? fqName.substring(dotIndex + 1) : "";
316 else {
317 packageName = "";
318 className = fqName;
322 PsiClass aClass = JavaPsiFacade.getInstance(manager.getProject()).findClass(fqName, GlobalSearchScope.projectScope(myProject));
323 if (aClass != null) return aClass;
325 final PsiDirectory directory = PackageUtil.findOrCreateDirectoryForPackage(
326 myProject,
327 packageName,
328 mySourceClass.getContainingFile().getContainingDirectory(),
329 true);
331 if (directory == null) {
332 return null;
335 int answer = Messages.showYesNoDialog(
336 myProject,
337 RefactoringBundle.message("class.0.does.not.exist", fqName),
338 MoveMembersImpl.REFACTORING_NAME,
339 Messages.getQuestionIcon()
341 if (answer != 0) return null;
342 final Ref<IncorrectOperationException> eRef = new Ref<IncorrectOperationException>();
343 final PsiClass newClass = ApplicationManager.getApplication().runWriteAction(new Computable<PsiClass>() {
344 public PsiClass compute() {
345 try {
346 return JavaDirectoryService.getInstance().createClass(directory, className);
348 catch (IncorrectOperationException e) {
349 eRef.set(e);
350 return null;
354 if (!eRef.isNull()) throw eRef.get();
355 return newClass;
358 protected void doHelpAction() {
359 HelpManager.getInstance().invokeHelp(HelpID.MOVE_MEMBERS);
362 private class ChooseClassAction implements ActionListener {
363 public void actionPerformed(ActionEvent e) {
364 TreeClassChooser chooser = TreeClassChooserFactory.getInstance(myProject).createWithInnerClassesScopeChooser(
365 RefactoringBundle.message("choose.destination.class"), GlobalSearchScope.projectScope(myProject), new TreeClassChooser.ClassFilter() {
366 public boolean isAccepted(PsiClass aClass) {
367 return aClass.getParent() instanceof PsiFile || aClass.hasModifierProperty(PsiModifier.STATIC);
369 }, null);
370 final String targetClassName = getTargetClassName();
371 if (targetClassName != null) {
372 final PsiClass aClass = JavaPsiFacade.getInstance(myProject).findClass(targetClassName, GlobalSearchScope.allScope(myProject));
373 if (aClass != null) {
374 chooser.selectDirectory(aClass.getContainingFile().getContainingDirectory());
375 } else {
376 chooser.selectDirectory(mySourceClass.getContainingFile().getContainingDirectory());
380 chooser.showDialog();
381 PsiClass aClass = chooser.getSelectedClass();
382 if (aClass != null) {
383 myTfTargetClassName.setText(aClass.getQualifiedName());
384 myMemberInfoModel.updateTargetClass();
389 private class MyMemberInfoModel extends UsesAndInterfacesDependencyMemberInfoModel {
390 PsiClass myTargetClass = null;
391 public MyMemberInfoModel() {
392 super(mySourceClass, null, false, DEFAULT_CONTAINMENT_VERIFIER);
395 public Boolean isFixedAbstract(MemberInfo member) {
396 return null;
399 public boolean isCheckedWhenDisabled(MemberInfo member) {
400 return false;
403 public boolean isMemberEnabled(MemberInfo member) {
404 if(myTargetClass != null && myTargetClass.isInterface()) {
405 return !(member.getMember() instanceof PsiMethod);
407 return super.isMemberEnabled(member);
410 public void updateTargetClass() {
411 final PsiManager manager = PsiManager.getInstance(myProject);
412 myTargetClass =
413 JavaPsiFacade.getInstance(manager.getProject()).findClass(getTargetClassName(), GlobalSearchScope.projectScope(myProject));
414 myTable.fireExternalDataChange();
415 myIntroduceEnumConstants.setEnabled(myTargetClass != null && myTargetClass.isEnum());