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
.introduceField
;
18 import com
.intellij
.codeInsight
.AnnotationUtil
;
19 import com
.intellij
.codeInsight
.completion
.JavaCompletionUtil
;
20 import com
.intellij
.ide
.util
.PropertiesComponent
;
21 import com
.intellij
.ide
.util
.TreeClassChooser
;
22 import com
.intellij
.ide
.util
.TreeClassChooserFactory
;
23 import com
.intellij
.openapi
.diagnostic
.Logger
;
24 import com
.intellij
.openapi
.editor
.event
.DocumentAdapter
;
25 import com
.intellij
.openapi
.editor
.event
.DocumentEvent
;
26 import com
.intellij
.openapi
.help
.HelpManager
;
27 import com
.intellij
.openapi
.project
.Project
;
28 import com
.intellij
.openapi
.roots
.LanguageLevelProjectExtension
;
29 import com
.intellij
.openapi
.ui
.DialogWrapper
;
30 import com
.intellij
.openapi
.ui
.Messages
;
31 import com
.intellij
.openapi
.util
.Comparing
;
32 import com
.intellij
.psi
.*;
33 import com
.intellij
.psi
.codeStyle
.JavaCodeStyleManager
;
34 import com
.intellij
.psi
.codeStyle
.SuggestedNameInfo
;
35 import com
.intellij
.psi
.codeStyle
.VariableKind
;
36 import com
.intellij
.psi
.impl
.source
.resolve
.JavaResolveUtil
;
37 import com
.intellij
.psi
.search
.GlobalSearchScope
;
38 import com
.intellij
.refactoring
.HelpID
;
39 import com
.intellij
.refactoring
.JavaRefactoringSettings
;
40 import com
.intellij
.refactoring
.RefactoringBundle
;
41 import com
.intellij
.refactoring
.ui
.*;
42 import com
.intellij
.refactoring
.util
.CommonRefactoringUtil
;
43 import com
.intellij
.refactoring
.util
.EnumConstantsUtil
;
44 import com
.intellij
.refactoring
.util
.RefactoringMessageUtil
;
45 import com
.intellij
.ui
.RecentsManager
;
46 import com
.intellij
.ui
.ReferenceEditorComboWithBrowseButton
;
47 import com
.intellij
.ui
.StateRestoringCheckBox
;
48 import com
.intellij
.util
.IncorrectOperationException
;
49 import gnu
.trove
.THashSet
;
50 import org
.jetbrains
.annotations
.NonNls
;
54 import java
.awt
.event
.ActionEvent
;
55 import java
.awt
.event
.ActionListener
;
56 import java
.awt
.event
.ItemEvent
;
57 import java
.awt
.event
.ItemListener
;
58 import java
.util
.Iterator
;
59 import java
.util
.LinkedHashSet
;
62 class IntroduceConstantDialog
extends DialogWrapper
{
63 private static final Logger LOG
= Logger
.getInstance("#com.intellij.refactoring.introduceField.IntroduceConstantDialog");
64 @NonNls private static final String RECENTS_KEY
= "IntroduceConstantDialog.RECENTS_KEY";
65 @NonNls private static final String NONNLS_SELECTED_PROPERTY
= "INTRODUCE_CONSTANT_NONNLS";
67 private final Project myProject
;
68 private final PsiClass myParentClass
;
69 private final PsiExpression myInitializerExpression
;
70 private final PsiLocalVariable myLocalVariable
;
71 private final boolean myInvokedOnDeclaration
;
72 private final PsiExpression
[] myOccurrences
;
73 private final int myOccurrencesCount
;
74 private PsiClass myTargetClass
;
75 private final TypeSelectorManager myTypeSelectorManager
;
77 private NameSuggestionsField myNameField
;
78 private JCheckBox myCbReplaceAll
;
80 private JRadioButton myRbPrivate
;
81 private JRadioButton myRbProtected
;
82 private JRadioButton myRbpackageLocal
;
83 private JRadioButton myRbPublic
;
85 private TypeSelector myTypeSelector
;
86 private StateRestoringCheckBox myCbDeleteVariable
;
87 private final JavaCodeStyleManager myCodeStyleManager
;
88 private ReferenceEditorComboWithBrowseButton myTfTargetClassName
;
89 private BaseExpressionToFieldHandler
.TargetDestination myDestinationClass
;
90 private JPanel myTypePanel
;
91 private JPanel myTargetClassNamePanel
;
92 private JPanel myPanel
;
93 private JLabel myTypeLabel
;
94 private JPanel myNameSuggestionPanel
;
95 private JLabel myNameSuggestionLabel
;
96 private JLabel myTargetClassNameLabel
;
97 private JCheckBox myCbNonNls
;
98 private final JCheckBox myIntroduceEnumConstantCb
= new JCheckBox(RefactoringBundle
.message("introduce.constant.enum.cb"), true);
100 IntroduceConstantDialog(Project project
,
101 PsiClass parentClass
,
102 PsiExpression initializerExpression
,
103 PsiLocalVariable localVariable
,
104 boolean isInvokedOnDeclaration
,
105 PsiExpression
[] occurrences
,
106 PsiClass targetClass
,
107 TypeSelectorManager typeSelectorManager
) {
108 super(project
, true);
110 myParentClass
= parentClass
;
111 myInitializerExpression
= initializerExpression
;
112 myLocalVariable
= localVariable
;
113 myInvokedOnDeclaration
= isInvokedOnDeclaration
;
114 myOccurrences
= occurrences
;
115 myOccurrencesCount
= occurrences
.length
;
116 myTargetClass
= targetClass
;
117 myTypeSelectorManager
= typeSelectorManager
;
118 myDestinationClass
= null;
120 setTitle(IntroduceConstantHandler
.REFACTORING_NAME
);
121 myCodeStyleManager
= JavaCodeStyleManager
.getInstance(myProject
);
124 final String ourLastVisibility
= JavaRefactoringSettings
.getInstance().INTRODUCE_CONSTANT_VISIBILITY
;
125 if (PsiModifier
.PUBLIC
.equals(ourLastVisibility
)) {
126 myRbPublic
.setSelected(true);
127 } else if (PsiModifier
.PROTECTED
.equals(ourLastVisibility
)) {
128 myRbProtected
.setSelected(true);
129 } else if (PsiModifier
.PACKAGE_LOCAL
.equals(ourLastVisibility
)) {
130 myRbpackageLocal
.setSelected(true);
131 } else if (PsiModifier
.PRIVATE
.equals(ourLastVisibility
)) {
132 myRbPrivate
.setSelected(true);
134 myRbPrivate
.setSelected(true);
136 myIntroduceEnumConstantCb
.setEnabled(EnumConstantsUtil
.isSuitableForEnumConstant(getSelectedType(), myTargetClass
));
137 updateVisibilityPanel();
140 public String
getEnteredName() {
141 return myNameField
.getEnteredName();
144 private String
getTargetClassName() {
145 return myTfTargetClassName
.getText().trim();
148 public BaseExpressionToFieldHandler
.TargetDestination
getDestinationClass () {
149 return myDestinationClass
;
152 public boolean introduceEnumConstant() {
153 return myIntroduceEnumConstantCb
.isEnabled() && myIntroduceEnumConstantCb
.isSelected();
157 public String
getFieldVisibility() {
158 if (myRbPublic
.isSelected()) {
159 return PsiModifier
.PUBLIC
;
161 if (myRbpackageLocal
.isSelected()) {
162 return PsiModifier
.PACKAGE_LOCAL
;
164 if (myRbProtected
.isSelected()) {
165 return PsiModifier
.PROTECTED
;
167 if (myRbPrivate
.isSelected()) {
168 return PsiModifier
.PRIVATE
;
170 LOG
.assertTrue(false);
174 public boolean isReplaceAllOccurrences() {
175 return myOccurrencesCount
> 1 && myCbReplaceAll
.isSelected();
178 public PsiType
getSelectedType() {
179 return myTypeSelector
.getSelectedType();
182 protected Action
[] createActions() {
183 return new Action
[]{getOKAction(), getCancelAction(), getHelpAction()};
186 protected void doHelpAction() {
187 HelpManager
.getInstance().invokeHelp(HelpID
.INTRODUCE_CONSTANT
);
190 protected JComponent
createNorthPanel() {
191 myTypeSelector
= myTypeSelectorManager
.getTypeSelector();
192 myTypePanel
.setLayout(new BorderLayout());
193 myTypePanel
.add(myTypeSelector
.getComponent(), BorderLayout
.CENTER
);
194 if (myTypeSelector
.getFocusableComponent() != null) {
195 myTypeLabel
.setLabelFor(myTypeSelector
.getFocusableComponent());
198 myNameField
= new NameSuggestionsField(myProject
);
199 myNameSuggestionPanel
.setLayout(new BorderLayout());
201 myNameSuggestionPanel
.add(myNameField
.getComponent(), BorderLayout
.CENTER
);
202 myNameSuggestionLabel
.setLabelFor(myNameField
.getFocusableComponent());
204 Set
<String
> possibleClassNames
= new LinkedHashSet
<String
>();
205 for (final PsiExpression occurrence
: myOccurrences
) {
206 final PsiClass parentClass
= new IntroduceConstantHandler().getParentClass(occurrence
);
207 if (parentClass
!= null && parentClass
.getQualifiedName() != null) {
208 possibleClassNames
.add(parentClass
.getQualifiedName());
211 myTfTargetClassName
=
212 new ReferenceEditorComboWithBrowseButton(new ChooseClassAction(), "", PsiManager
.getInstance(myProject
), true, RECENTS_KEY
);
213 myTargetClassNamePanel
.setLayout(new BorderLayout());
214 myTargetClassNamePanel
.add(myTfTargetClassName
, BorderLayout
.CENTER
);
215 myTargetClassNameLabel
.setLabelFor(myTfTargetClassName
);
216 for (String possibleClassName
: possibleClassNames
) {
217 myTfTargetClassName
.prependItem(possibleClassName
);
219 myTfTargetClassName
.getChildComponent().addDocumentListener(new DocumentAdapter() {
220 public void documentChanged(DocumentEvent e
) {
221 targetClassChanged();
224 myIntroduceEnumConstantCb
.addActionListener(new ActionListener() {
225 public void actionPerformed(final ActionEvent e
) {
226 enableEnumDependant(introduceEnumConstant());
229 enableEnumDependant(introduceEnumConstant());
230 final JPanel enumPanel
= new JPanel(new BorderLayout());
231 enumPanel
.add(myIntroduceEnumConstantCb
, BorderLayout
.EAST
);
232 myTargetClassNamePanel
.add(enumPanel
, BorderLayout
.SOUTH
);
234 final String propertyName
;
235 if (myLocalVariable
!= null) {
236 propertyName
= myCodeStyleManager
.variableNameToPropertyName(myLocalVariable
.getName(), VariableKind
.LOCAL_VARIABLE
);
241 final NameSuggestionsManager nameSuggestionsManager
=
242 new NameSuggestionsManager(myTypeSelector
, myNameField
, new NameSuggestionsGenerator() {
243 public SuggestedNameInfo
getSuggestedNameInfo(PsiType type
) {
244 final SuggestedNameInfo nameInfo
=
245 myCodeStyleManager
.suggestVariableName(VariableKind
.STATIC_FINAL_FIELD
, propertyName
, myInitializerExpression
, type
);
246 final String
[] strings
= JavaCompletionUtil
.completeVariableNameForRefactoring(myCodeStyleManager
, type
, VariableKind
.LOCAL_VARIABLE
, nameInfo
);
247 return new SuggestedNameInfo
.Delegate(strings
, nameInfo
);
252 nameSuggestionsManager
.setLabelsFor(myTypeLabel
, myNameSuggestionLabel
);
254 if (myOccurrencesCount
> 1) {
255 myCbReplaceAll
.addItemListener(new ItemListener() {
256 public void itemStateChanged(ItemEvent e
) {
257 updateTypeSelector();
259 myNameField
.requestFocusInWindow();
262 myCbReplaceAll
.setText(RefactoringBundle
.message("replace.all.occurences", myOccurrencesCount
));
265 myCbReplaceAll
.setVisible(false);
268 if (myLocalVariable
!= null) {
269 if (myInvokedOnDeclaration
) {
270 myCbDeleteVariable
.setEnabled(false);
271 myCbDeleteVariable
.setSelected(true);
273 else if (myCbReplaceAll
!= null) {
274 updateCbDeleteVariable();
275 myCbReplaceAll
.addItemListener(
277 public void itemStateChanged(ItemEvent e
) {
278 updateCbDeleteVariable();
284 myCbDeleteVariable
.setVisible(false);
287 final PsiManager psiManager
= PsiManager
.getInstance(myProject
);
288 if (myTypeSelectorManager
.isSuggestedType("java.lang.String") &&
289 LanguageLevelProjectExtension
.getInstance(psiManager
.getProject()).getLanguageLevel().hasEnumKeywordAndAutoboxing() &&
290 JavaPsiFacade
.getInstance(psiManager
.getProject()).findClass(AnnotationUtil
.NON_NLS
, myParentClass
.getResolveScope()) != null) {
291 final PropertiesComponent component
= PropertiesComponent
.getInstance(myProject
);
292 myCbNonNls
.setSelected(component
.isTrueValue(NONNLS_SELECTED_PROPERTY
));
293 myCbNonNls
.addItemListener(new ItemListener() {
294 public void itemStateChanged(ItemEvent e
) {
295 component
.setValue(NONNLS_SELECTED_PROPERTY
, Boolean
.toString(myCbNonNls
.isSelected()));
299 myCbNonNls
.setVisible(false);
302 updateTypeSelector();
304 ButtonGroup bg
= new ButtonGroup();
306 bg
.add(myRbpackageLocal
);
307 bg
.add(myRbProtected
);
313 private void targetClassChanged() {
314 final String targetClassName
= getTargetClassName();
315 myTargetClass
= JavaPsiFacade
.getInstance(myProject
).findClass(targetClassName
, GlobalSearchScope
.projectScope(myProject
));
316 updateVisibilityPanel();
317 myIntroduceEnumConstantCb
.setEnabled(EnumConstantsUtil
.isSuitableForEnumConstant(getSelectedType(), myTargetClass
));
320 private void enableEnumDependant(boolean enable
) {
322 myRbPrivate
.setEnabled(false);
323 myRbProtected
.setEnabled(false);
324 myRbpackageLocal
.setEnabled(false);
325 myRbPublic
.setEnabled(true);
326 myRbPublic
.setSelected(true);
328 updateVisibilityPanel();
330 myCbNonNls
.setEnabled(!enable
);
333 protected JComponent
createCenterPanel() {
337 public boolean isDeleteVariable() {
338 return myInvokedOnDeclaration
|| myCbDeleteVariable
!= null && myCbDeleteVariable
.isSelected();
341 public boolean isAnnotateAsNonNls() {
342 return myCbNonNls
!= null && myCbNonNls
.isSelected();
345 private void updateCbDeleteVariable() {
346 if (!myCbReplaceAll
.isSelected()) {
347 myCbDeleteVariable
.makeUnselectable(false);
350 myCbDeleteVariable
.makeSelectable();
354 private void updateTypeSelector() {
355 if (myCbReplaceAll
!= null) {
356 myTypeSelectorManager
.setAllOccurences(myCbReplaceAll
.isSelected());
359 myTypeSelectorManager
.setAllOccurences(false);
363 private void updateVisibilityPanel() {
364 if (myTargetClass
== null) return;
365 if (myTargetClass
.isInterface()) {
366 myRbPrivate
.setEnabled(false);
367 myRbProtected
.setEnabled(false);
368 myRbpackageLocal
.setEnabled(false);
369 myRbPublic
.setEnabled(true);
370 myRbPublic
.setSelected(true);
373 myRbPrivate
.setEnabled(true);
374 myRbProtected
.setEnabled(true);
375 myRbpackageLocal
.setEnabled(true);
376 myRbPublic
.setEnabled(true);
377 // exclude all modifiers not visible from all occurences
378 final Set
<String
> visible
= new THashSet
<String
>();
379 visible
.add(PsiModifier
.PRIVATE
);
380 visible
.add(PsiModifier
.PROTECTED
);
381 visible
.add(PsiModifier
.PACKAGE_LOCAL
);
382 visible
.add(PsiModifier
.PUBLIC
);
383 for (PsiExpression occurrence
: myOccurrences
) {
384 final PsiManager psiManager
= PsiManager
.getInstance(myProject
);
385 for (Iterator
<String
> iterator
= visible
.iterator(); iterator
.hasNext();) {
386 String modifier
= iterator
.next();
389 final String modifierText
= PsiModifier
.PACKAGE_LOCAL
.equals(modifier
) ?
"" : modifier
;
390 final PsiField field
= JavaPsiFacade
.getInstance(psiManager
.getProject()).getElementFactory().createFieldFromText(modifierText
+ " int xxx;", myTargetClass
);
391 if (!JavaResolveUtil
.isAccessible(field
, myTargetClass
, field
.getModifierList(), occurrence
, myTargetClass
, null)) {
395 catch (IncorrectOperationException e
) {
400 if (visible
.contains(PsiModifier
.PUBLIC
)) myRbPublic
.setSelected(true);
401 if (visible
.contains(PsiModifier
.PACKAGE_LOCAL
)) myRbpackageLocal
.setSelected(true);
402 if (visible
.contains(PsiModifier
.PROTECTED
)) myRbProtected
.setSelected(true);
403 if (visible
.contains(PsiModifier
.PRIVATE
)) myRbPrivate
.setSelected(true);
407 protected void doOKAction() {
408 final String targetClassName
= getTargetClassName();
409 PsiClass newClass
= myParentClass
;
411 if (!"".equals (targetClassName
) && !Comparing
.strEqual(targetClassName
, myParentClass
.getQualifiedName())) {
412 newClass
= JavaPsiFacade
.getInstance(myProject
).findClass(targetClassName
, GlobalSearchScope
.projectScope(myProject
));
413 if (newClass
== null) {
414 if (Messages
.showOkCancelDialog(myProject
, RefactoringBundle
.message("class.does.not.exist.in.the.project"), IntroduceConstantHandler
.REFACTORING_NAME
, Messages
.getErrorIcon()) != OK_EXIT_CODE
) {
417 myDestinationClass
= new BaseExpressionToFieldHandler
.TargetDestination(targetClassName
, myParentClass
);
419 myDestinationClass
= new BaseExpressionToFieldHandler
.TargetDestination(newClass
);
423 String fieldName
= getEnteredName();
424 String errorString
= null;
425 if ("".equals(fieldName
)) {
426 errorString
= RefactoringBundle
.message("no.field.name.specified");
427 } else if (!JavaPsiFacade
.getInstance(myProject
).getNameHelper().isIdentifier(fieldName
)) {
428 errorString
= RefactoringMessageUtil
.getIncorrectIdentifierMessage(fieldName
);
430 if (errorString
!= null) {
431 CommonRefactoringUtil
.showErrorMessage(
432 IntroduceFieldHandler
.REFACTORING_NAME
,
434 HelpID
.INTRODUCE_FIELD
,
438 if (newClass
!= null) {
439 PsiField oldField
= newClass
.findFieldByName(fieldName
, true);
441 if (oldField
!= null) {
442 int answer
= Messages
.showYesNoDialog(
444 RefactoringBundle
.message("field.exists", fieldName
, oldField
.getContainingClass().getQualifiedName()),
445 IntroduceFieldHandler
.REFACTORING_NAME
,
446 Messages
.getWarningIcon()
454 JavaRefactoringSettings
.getInstance().INTRODUCE_CONSTANT_VISIBILITY
= getFieldVisibility();
456 RecentsManager
.getInstance(myProject
).registerRecentEntry(RECENTS_KEY
, targetClassName
);
460 public JComponent
getPreferredFocusedComponent() {
461 return myNameField
.getFocusableComponent();
464 private class ChooseClassAction
implements ActionListener
{
465 public void actionPerformed(ActionEvent e
) {
466 TreeClassChooser chooser
= TreeClassChooserFactory
.getInstance(myProject
).createWithInnerClassesScopeChooser(RefactoringBundle
.message("choose.destination.class"), GlobalSearchScope
.projectScope(myProject
), new TreeClassChooser
.ClassFilter() {
467 public boolean isAccepted(PsiClass aClass
) {
468 return aClass
.getParent() instanceof PsiJavaFile
|| aClass
.hasModifierProperty(PsiModifier
.STATIC
);
471 if (myTargetClass
!= null) {
472 chooser
.selectDirectory(myTargetClass
.getContainingFile().getContainingDirectory());
474 chooser
.showDialog();
475 PsiClass aClass
= chooser
.getSelectedClass();
476 if (aClass
!= null) {
477 myTfTargetClassName
.setText(aClass
.getQualifiedName());