update copyright
[fedora-idea.git] / java / java-impl / src / com / intellij / refactoring / move / moveMembers / MoveMembersDialog.java
blobb5bcb014ddc7c0571011393d5d68214327d3f690
1 /*
2 * Copyright 2000-2009 JetBrains s.r.o.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
16 package com.intellij.refactoring.move.moveMembers;
18 import com.intellij.ide.util.PackageUtil;
19 import com.intellij.ide.util.TreeClassChooser;
20 import com.intellij.ide.util.TreeClassChooserFactory;
21 import com.intellij.openapi.application.ApplicationManager;
22 import com.intellij.openapi.command.CommandProcessor;
23 import com.intellij.openapi.help.HelpManager;
24 import com.intellij.openapi.options.ConfigurationException;
25 import com.intellij.openapi.project.Project;
26 import com.intellij.openapi.ui.Messages;
27 import com.intellij.openapi.util.Computable;
28 import com.intellij.openapi.util.Ref;
29 import com.intellij.psi.*;
30 import com.intellij.psi.search.GlobalSearchScope;
31 import com.intellij.psi.util.PsiTreeUtil;
32 import com.intellij.refactoring.HelpID;
33 import com.intellij.refactoring.JavaRefactoringSettings;
34 import com.intellij.refactoring.RefactoringBundle;
35 import com.intellij.refactoring.classMembers.MemberInfoChange;
36 import com.intellij.refactoring.move.MoveCallback;
37 import com.intellij.refactoring.ui.MemberSelectionTable;
38 import com.intellij.refactoring.ui.RefactoringDialog;
39 import com.intellij.refactoring.ui.VisibilityPanel;
40 import com.intellij.refactoring.util.CommonRefactoringUtil;
41 import com.intellij.refactoring.util.classMembers.MemberInfo;
42 import com.intellij.refactoring.util.classMembers.UsesAndInterfacesDependencyMemberInfoModel;
43 import com.intellij.ui.IdeBorderFactory;
44 import com.intellij.ui.RecentsManager;
45 import com.intellij.ui.ReferenceEditorComboWithBrowseButton;
46 import com.intellij.ui.ScrollPaneFactory;
47 import com.intellij.usageView.UsageViewUtil;
48 import com.intellij.util.IncorrectOperationException;
49 import org.jetbrains.annotations.NonNls;
51 import javax.swing.*;
52 import javax.swing.border.Border;
53 import java.awt.*;
54 import java.awt.event.ActionEvent;
55 import java.awt.event.ActionListener;
56 import java.util.ArrayList;
57 import java.util.Collection;
58 import java.util.List;
59 import java.util.Set;
61 public class MoveMembersDialog extends RefactoringDialog implements MoveMembersOptions {
62 @NonNls private static final String RECENTS_KEY = "MoveMembersDialog.RECENTS_KEY";
63 private MyMemberInfoModel myMemberInfoModel;
65 private final Project myProject;
66 private final PsiClass mySourceClass;
67 private final String mySourceClassName;
68 private final List<MemberInfo> myMemberInfos;
69 private final ReferenceEditorComboWithBrowseButton myTfTargetClassName;
70 private MemberSelectionTable myTable;
71 private final MoveCallback myMoveCallback;
73 VisibilityPanel myVisibilityPanel;
74 private final JCheckBox myIntroduceEnumConstants = new JCheckBox(RefactoringBundle.message("move.enum.constant.cb"), true);
76 public MoveMembersDialog(Project project,
77 PsiClass sourceClass,
78 final PsiClass initialTargetClass,
79 Set<PsiMember> preselectMembers,
80 MoveCallback moveCallback) {
81 super(project, true);
82 myProject = project;
83 mySourceClass = sourceClass;
84 myMoveCallback = moveCallback;
85 setTitle(MoveMembersImpl.REFACTORING_NAME);
87 mySourceClassName = mySourceClass.getQualifiedName();
89 PsiField[] fields = mySourceClass.getFields();
90 PsiMethod[] methods = mySourceClass.getMethods();
91 PsiClass[] innerClasses = mySourceClass.getInnerClasses();
92 ArrayList<MemberInfo> memberList = new ArrayList<MemberInfo>(fields.length + methods.length);
94 for (PsiClass innerClass : innerClasses) {
95 if (!innerClass.hasModifierProperty(PsiModifier.STATIC)) continue;
96 MemberInfo info = new MemberInfo(innerClass);
97 if (preselectMembers.contains(innerClass)) {
98 info.setChecked(true);
100 memberList.add(info);
102 boolean hasConstantFields = false;
103 for (PsiField field : fields) {
104 if (field.hasModifierProperty(PsiModifier.STATIC)) {
105 MemberInfo info = new MemberInfo(field);
106 if (preselectMembers.contains(field)) {
107 info.setChecked(true);
109 memberList.add(info);
110 hasConstantFields = true;
113 if (!hasConstantFields) myIntroduceEnumConstants.setVisible(false);
114 for (PsiMethod method : methods) {
115 if (method.hasModifierProperty(PsiModifier.STATIC)) {
116 MemberInfo info = new MemberInfo(method);
117 if (preselectMembers.contains(method)) {
118 info.setChecked(true);
120 memberList.add(info);
123 myMemberInfos = memberList;
124 String fqName = initialTargetClass != null && !sourceClass.equals(initialTargetClass) ? initialTargetClass.getQualifiedName() : "";
125 myTfTargetClassName = new ReferenceEditorComboWithBrowseButton(new ChooseClassAction(), fqName, PsiManager.getInstance(myProject), true, RECENTS_KEY);
127 init();
130 public String getMemberVisibility() {
131 return myVisibilityPanel.getVisibility();
134 public boolean makeEnumConstant() {
135 return myIntroduceEnumConstants.isVisible() && myIntroduceEnumConstants.isEnabled() && myIntroduceEnumConstants.isSelected();
138 protected String getDimensionServiceKey() {
139 return "#com.intellij.refactoring.move.moveMembers.MoveMembersDialog";
142 private JTable createTable() {
143 myMemberInfoModel = new MyMemberInfoModel();
144 myTable = new MemberSelectionTable(myMemberInfos, null);
145 myTable.setMemberInfoModel(myMemberInfoModel);
146 myTable.addMemberInfoChangeListener(myMemberInfoModel);
147 myMemberInfoModel.memberInfoChanged(new MemberInfoChange<PsiMember, MemberInfo>(myMemberInfos));
148 return myTable;
151 protected JComponent createNorthPanel() {
152 JPanel panel = new JPanel(new BorderLayout());
154 JPanel _panel;
155 Box box = Box.createVerticalBox();
157 _panel = new JPanel(new BorderLayout());
158 JTextField sourceClassField = new JTextField();
159 sourceClassField.setText(mySourceClassName);
160 sourceClassField.setEditable(false);
161 _panel.add(new JLabel(RefactoringBundle.message("move.members.move.members.from.label")), BorderLayout.NORTH);
162 _panel.add(sourceClassField, BorderLayout.CENTER);
163 box.add(_panel);
165 box.add(Box.createVerticalStrut(10));
167 _panel = new JPanel(new BorderLayout());
168 JLabel label = new JLabel(RefactoringBundle.message("move.members.to.fully.qualified.name.label"));
169 label.setLabelFor(myTfTargetClassName);
170 _panel.add(label, BorderLayout.NORTH);
171 _panel.add(myTfTargetClassName, BorderLayout.CENTER);
172 _panel.add(myIntroduceEnumConstants, BorderLayout.SOUTH);
173 box.add(_panel);
175 myTfTargetClassName.getChildComponent().getDocument().addDocumentListener(new com.intellij.openapi.editor.event.DocumentAdapter() {
176 public void documentChanged(com.intellij.openapi.editor.event.DocumentEvent e) {
177 myMemberInfoModel.updateTargetClass();
178 validateButtons();
182 panel.add(box, BorderLayout.CENTER);
183 panel.add(Box.createVerticalStrut(10), BorderLayout.SOUTH);
185 validateButtons();
186 return panel;
189 protected JComponent createCenterPanel() {
190 JPanel panel = new JPanel(new BorderLayout());
191 JTable table = createTable();
192 if (table.getRowCount() > 0) {
193 table.getSelectionModel().addSelectionInterval(0, 0);
195 JScrollPane scrollPane = ScrollPaneFactory.createScrollPane(table);
196 Border titledBorder = IdeBorderFactory.createTitledBorder(RefactoringBundle.message("move.members.members.to.be.moved.border.title"));
197 Border emptyBorder = BorderFactory.createEmptyBorder(0, 5, 5, 5);
198 Border border = BorderFactory.createCompoundBorder(titledBorder, emptyBorder);
199 scrollPane.setBorder(border);
200 panel.add(scrollPane, BorderLayout.CENTER);
202 myVisibilityPanel = new VisibilityPanel(true, true);
203 myVisibilityPanel.setVisibility(null);
204 panel.add(myVisibilityPanel, BorderLayout.EAST);
206 return panel;
209 public JComponent getPreferredFocusedComponent() {
210 return myTfTargetClassName.getChildComponent();
213 public PsiMember[] getSelectedMembers() {
214 final Collection<MemberInfo> selectedMemberInfos = myTable.getSelectedMemberInfos();
215 ArrayList<PsiMember> list = new ArrayList<PsiMember>();
216 for (MemberInfo selectedMemberInfo : selectedMemberInfos) {
217 list.add(selectedMemberInfo.getMember());
219 return list.toArray(new PsiMember[list.size()]);
222 public String getTargetClassName() {
223 return myTfTargetClassName.getText();
226 protected void doAction() {
227 String message = validateInputData();
229 if (message != null) {
230 if (message.length() != 0) {
231 CommonRefactoringUtil.showErrorMessage(
232 MoveMembersImpl.REFACTORING_NAME,
233 message,
234 HelpID.MOVE_MEMBERS,
235 myProject);
237 return;
240 invokeRefactoring(new MoveMembersProcessor(getProject(), myMoveCallback, new MoveMembersOptions() {
241 public String getMemberVisibility() {
242 return MoveMembersDialog.this.getMemberVisibility();
245 public boolean makeEnumConstant() {
246 return MoveMembersDialog.this.makeEnumConstant();
249 public PsiMember[] getSelectedMembers() {
250 return MoveMembersDialog.this.getSelectedMembers();
253 public String getTargetClassName() {
254 return MoveMembersDialog.this.getTargetClassName();
256 }));
258 JavaRefactoringSettings.getInstance().MOVE_PREVIEW_USAGES = isPreviewUsages();
261 @Override
262 protected void canRun() throws ConfigurationException {
263 if (getTargetClassName().length() == 0) throw new ConfigurationException("Destination class name not found");
266 private String validateInputData() {
267 final PsiManager manager = PsiManager.getInstance(myProject);
268 final String fqName = getTargetClassName();
269 if ("".equals(fqName)) {
270 return RefactoringBundle.message("no.destination.class.specified");
272 else {
273 if (!JavaPsiFacade.getInstance(manager.getProject()).getNameHelper().isQualifiedName(fqName)) {
274 return RefactoringBundle.message("0.is.not.a.legal.fq.name", fqName);
276 else {
277 RecentsManager.getInstance(myProject).registerRecentEntry(RECENTS_KEY, fqName);
278 final PsiClass[] targetClass = new PsiClass[]{null};
279 CommandProcessor.getInstance().executeCommand(myProject, new Runnable() {
280 public void run() {
281 try {
282 targetClass[0] = findOrCreateTargetClass(manager, fqName);
284 catch (IncorrectOperationException e) {
285 CommonRefactoringUtil.showErrorMessage(
286 MoveMembersImpl.REFACTORING_NAME,
287 e.getMessage(),
288 HelpID.MOVE_MEMBERS,
289 myProject);
293 }, RefactoringBundle.message("create.class.command", fqName), null);
295 if (targetClass[0] == null) {
296 return "";
299 if (mySourceClass.equals(targetClass[0])) {
300 return RefactoringBundle.message("source.and.destination.classes.should.be.different");
301 } else if (!mySourceClass.getLanguage().equals(targetClass[0].getLanguage())) {
302 return RefactoringBundle.message("move.to.different.language", UsageViewUtil.getType(mySourceClass), mySourceClass.getQualifiedName(), targetClass[0].getQualifiedName());
304 else {
305 for (MemberInfo info : myMemberInfos) {
306 if (!info.isChecked()) continue;
307 if (PsiTreeUtil.isAncestor(info.getMember(), targetClass[0], false)) {
308 return RefactoringBundle.message("cannot.move.inner.class.0.into.itself", info.getDisplayName());
312 if (!targetClass[0].isWritable()) {
313 if (!CommonRefactoringUtil.checkReadOnlyStatus(myProject, targetClass[0])) return "";
314 return "";
317 return null;
323 private PsiClass findOrCreateTargetClass(final PsiManager manager, final String fqName) throws IncorrectOperationException {
324 final String className;
325 final String packageName;
326 int dotIndex = fqName.lastIndexOf('.');
327 if (dotIndex >= 0) {
328 packageName = fqName.substring(0, dotIndex);
329 className = (dotIndex + 1 < fqName.length())? fqName.substring(dotIndex + 1) : "";
331 else {
332 packageName = "";
333 className = fqName;
337 PsiClass aClass = JavaPsiFacade.getInstance(manager.getProject()).findClass(fqName, GlobalSearchScope.projectScope(myProject));
338 if (aClass != null) return aClass;
340 final PsiDirectory directory = PackageUtil.findOrCreateDirectoryForPackage(
341 myProject,
342 packageName,
343 mySourceClass.getContainingFile().getContainingDirectory(),
344 true);
346 if (directory == null) {
347 return null;
350 int answer = Messages.showYesNoDialog(
351 myProject,
352 RefactoringBundle.message("class.0.does.not.exist", fqName),
353 MoveMembersImpl.REFACTORING_NAME,
354 Messages.getQuestionIcon()
356 if (answer != 0) return null;
357 final Ref<IncorrectOperationException> eRef = new Ref<IncorrectOperationException>();
358 final PsiClass newClass = ApplicationManager.getApplication().runWriteAction(new Computable<PsiClass>() {
359 public PsiClass compute() {
360 try {
361 return JavaDirectoryService.getInstance().createClass(directory, className);
363 catch (IncorrectOperationException e) {
364 eRef.set(e);
365 return null;
369 if (!eRef.isNull()) throw eRef.get();
370 return newClass;
373 protected void doHelpAction() {
374 HelpManager.getInstance().invokeHelp(HelpID.MOVE_MEMBERS);
377 private class ChooseClassAction implements ActionListener {
378 public void actionPerformed(ActionEvent e) {
379 TreeClassChooser chooser = TreeClassChooserFactory.getInstance(myProject).createWithInnerClassesScopeChooser(
380 RefactoringBundle.message("choose.destination.class"), GlobalSearchScope.projectScope(myProject), new TreeClassChooser.ClassFilter() {
381 public boolean isAccepted(PsiClass aClass) {
382 return aClass.getParent() instanceof PsiFile || aClass.hasModifierProperty(PsiModifier.STATIC);
384 }, null);
385 final String targetClassName = getTargetClassName();
386 if (targetClassName != null) {
387 final PsiClass aClass = JavaPsiFacade.getInstance(myProject).findClass(targetClassName, GlobalSearchScope.allScope(myProject));
388 if (aClass != null) {
389 chooser.selectDirectory(aClass.getContainingFile().getContainingDirectory());
390 } else {
391 chooser.selectDirectory(mySourceClass.getContainingFile().getContainingDirectory());
395 chooser.showDialog();
396 PsiClass aClass = chooser.getSelectedClass();
397 if (aClass != null) {
398 myTfTargetClassName.setText(aClass.getQualifiedName());
399 myMemberInfoModel.updateTargetClass();
404 private class MyMemberInfoModel extends UsesAndInterfacesDependencyMemberInfoModel {
405 PsiClass myTargetClass = null;
406 public MyMemberInfoModel() {
407 super(mySourceClass, null, false, DEFAULT_CONTAINMENT_VERIFIER);
410 public Boolean isFixedAbstract(MemberInfo member) {
411 return null;
414 public boolean isCheckedWhenDisabled(MemberInfo member) {
415 return false;
418 public boolean isMemberEnabled(MemberInfo member) {
419 if(myTargetClass != null && myTargetClass.isInterface()) {
420 return !(member.getMember() instanceof PsiMethod);
422 return super.isMemberEnabled(member);
425 public void updateTargetClass() {
426 final PsiManager manager = PsiManager.getInstance(myProject);
427 myTargetClass =
428 JavaPsiFacade.getInstance(manager.getProject()).findClass(getTargetClassName(), GlobalSearchScope.projectScope(myProject));
429 myTable.fireExternalDataChange();
430 myIntroduceEnumConstants.setEnabled(myTargetClass != null && myTargetClass.isEnum());