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
;
52 import javax
.swing
.border
.Border
;
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
;
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
,
78 final PsiClass initialTargetClass
,
79 Set
<PsiMember
> preselectMembers
,
80 MoveCallback moveCallback
) {
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
);
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
));
151 protected JComponent
createNorthPanel() {
152 JPanel panel
= new JPanel(new BorderLayout());
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
);
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
);
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();
182 panel
.add(box
, BorderLayout
.CENTER
);
183 panel
.add(Box
.createVerticalStrut(10), BorderLayout
.SOUTH
);
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
);
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
,
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();
258 JavaRefactoringSettings
.getInstance().MOVE_PREVIEW_USAGES
= isPreviewUsages();
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");
273 if (!JavaPsiFacade
.getInstance(manager
.getProject()).getNameHelper().isQualifiedName(fqName
)) {
274 return RefactoringBundle
.message("0.is.not.a.legal.fq.name", fqName
);
277 RecentsManager
.getInstance(myProject
).registerRecentEntry(RECENTS_KEY
, fqName
);
278 final PsiClass
[] targetClass
= new PsiClass
[]{null};
279 CommandProcessor
.getInstance().executeCommand(myProject
, new Runnable() {
282 targetClass
[0] = findOrCreateTargetClass(manager
, fqName
);
284 catch (IncorrectOperationException e
) {
285 CommonRefactoringUtil
.showErrorMessage(
286 MoveMembersImpl
.REFACTORING_NAME
,
293 }, RefactoringBundle
.message("create.class.command", fqName
), null);
295 if (targetClass
[0] == null) {
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());
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 "";
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('.');
328 packageName
= fqName
.substring(0, dotIndex
);
329 className
= (dotIndex
+ 1 < fqName
.length())? fqName
.substring(dotIndex
+ 1) : "";
337 PsiClass aClass
= JavaPsiFacade
.getInstance(manager
.getProject()).findClass(fqName
, GlobalSearchScope
.projectScope(myProject
));
338 if (aClass
!= null) return aClass
;
340 final PsiDirectory directory
= PackageUtil
.findOrCreateDirectoryForPackage(
343 mySourceClass
.getContainingFile().getContainingDirectory(),
346 if (directory
== null) {
350 int answer
= Messages
.showYesNoDialog(
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() {
361 return JavaDirectoryService
.getInstance().createClass(directory
, className
);
363 catch (IncorrectOperationException e
) {
369 if (!eRef
.isNull()) throw eRef
.get();
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
);
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());
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
) {
414 public boolean isCheckedWhenDisabled(MemberInfo member
) {
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
);
428 JavaPsiFacade
.getInstance(manager
.getProject()).findClass(getTargetClassName(), GlobalSearchScope
.projectScope(myProject
));
429 myTable
.fireExternalDataChange();
430 myIntroduceEnumConstants
.setEnabled(myTargetClass
!= null && myTargetClass
.isEnum());