1 package com
.intellij
.codeInsight
.generation
.ui
;
3 import com
.intellij
.codeInsight
.CodeInsightBundle
;
4 import com
.intellij
.codeInsight
.CodeInsightSettings
;
5 import com
.intellij
.codeInsight
.generation
.GenerateEqualsHelper
;
6 import com
.intellij
.ide
.wizard
.AbstractWizard
;
7 import com
.intellij
.ide
.wizard
.StepAdapter
;
8 import com
.intellij
.openapi
.diagnostic
.Logger
;
9 import com
.intellij
.openapi
.project
.Project
;
10 import com
.intellij
.openapi
.ui
.VerticalFlowLayout
;
11 import com
.intellij
.psi
.*;
12 import com
.intellij
.refactoring
.ui
.MemberSelectionPanel
;
13 import com
.intellij
.refactoring
.util
.classMembers
.MemberInfo
;
14 import com
.intellij
.refactoring
.util
.classMembers
.MemberInfoChange
;
15 import com
.intellij
.refactoring
.util
.classMembers
.MemberInfoModel
;
16 import com
.intellij
.refactoring
.util
.classMembers
.MemberInfoTooltipManager
;
17 import com
.intellij
.ui
.NonFocusableCheckBox
;
18 import com
.intellij
.util
.containers
.HashMap
;
19 import org
.jetbrains
.annotations
.NotNull
;
20 import org
.jetbrains
.annotations
.Nullable
;
23 import javax
.swing
.event
.TableModelEvent
;
24 import javax
.swing
.event
.TableModelListener
;
26 import java
.awt
.event
.ActionEvent
;
27 import java
.awt
.event
.ActionListener
;
28 import java
.util
.ArrayList
;
34 public class GenerateEqualsWizard
extends AbstractWizard
{
35 private static final Logger LOG
= Logger
.getInstance("#com.intellij.codeInsight.generation.ui.GenerateEqualsWizard");
36 private final PsiClass myClass
;
38 private final MemberSelectionPanel myEqualsPanel
;
39 private final MemberSelectionPanel myHashCodePanel
;
40 private final HashMap myFieldsToHashCode
;
41 private final MemberSelectionPanel myNonNullPanel
;
42 private final HashMap
<PsiElement
, MemberInfo
> myFieldsToNonNull
;
44 private final int myTestBoxedStep
;
45 private final int myEqualsStepCode
;
46 private final int myHashcodeStepCode
;
48 private final MemberInfo
[] myClassFields
;
49 private static final MyMemberInfoFilter MEMBER_INFO_FILTER
= new MyMemberInfoFilter();
52 public GenerateEqualsWizard(Project project
, PsiClass aClass
, boolean needEquals
, boolean needHashCode
) {
53 super(CodeInsightBundle
.message("generate.equals.hashcode.wizard.title"), project
);
54 LOG
.assertTrue(needEquals
|| needHashCode
);
57 myClassFields
= MemberInfo
.extractClassMembers(myClass
, MEMBER_INFO_FILTER
, false);
58 for (MemberInfo myClassField
: myClassFields
) {
59 myClassField
.setChecked(true);
61 int testBoxedStep
= 0;
64 new MemberSelectionPanel(CodeInsightBundle
.message("generate.equals.hashcode.equals.fields.chooser.title"), myClassFields
, null);
65 myEqualsPanel
.getTable().setMemberInfoModel(new EqualsMemberInfoModel());
72 final MemberInfo
[] hashCodeMemberInfos
;
74 myFieldsToHashCode
= createFieldToMemberInfoMap(true);
75 hashCodeMemberInfos
= new MemberInfo
[0];
78 hashCodeMemberInfos
= myClassFields
;
79 myFieldsToHashCode
= null;
81 myHashCodePanel
= new MemberSelectionPanel(CodeInsightBundle
.message("generate.equals.hashcode.hashcode.fields.chooser.title"),
82 hashCodeMemberInfos
, null);
83 myHashCodePanel
.getTable().setMemberInfoModel(new HashCodeMemberInfoModel());
85 updateHashCodeMemberInfos(myClassFields
);
90 myHashCodePanel
= null;
91 myFieldsToHashCode
= null;
93 myTestBoxedStep
=testBoxedStep
;
94 myNonNullPanel
= new MemberSelectionPanel(CodeInsightBundle
.message("generate.equals.hashcode.non.null.fields.chooser.title"),
95 new MemberInfo
[0], null);
96 myFieldsToNonNull
= createFieldToMemberInfoMap(false);
97 for (final Map
.Entry
<PsiElement
, MemberInfo
> entry
: myFieldsToNonNull
.entrySet()) {
98 entry
.getValue().setChecked(((PsiField
)entry
.getKey()).getModifierList().findAnnotation(NotNull
.class.getName()) != null);
101 final MyTableModelListener listener
= new MyTableModelListener();
102 if (myEqualsPanel
!= null) {
103 myEqualsPanel
.getTable().getModel().addTableModelListener(listener
);
104 addStep(new InstanceofOptionStep());
105 addStep(new MyStep(myEqualsPanel
));
106 myEqualsStepCode
= 1;
109 myEqualsStepCode
= -1;
112 if (myHashCodePanel
!= null) {
113 myHashCodePanel
.getTable().getModel().addTableModelListener(listener
);
114 addStep(new MyStep(myHashCodePanel
));
115 myHashcodeStepCode
= myEqualsStepCode
> 0 ? myEqualsStepCode
+ 1 : 1;
118 myHashcodeStepCode
= -1;
121 addStep(new MyStep(myNonNullPanel
));
127 public PsiField
[] getEqualsFields() {
128 if (myEqualsPanel
!= null) {
129 return memberInfosToFields(myEqualsPanel
.getTable().getSelectedMemberInfos());
136 public PsiField
[] getHashCodeFields() {
137 if (myHashCodePanel
!= null) {
138 return memberInfosToFields(myHashCodePanel
.getTable().getSelectedMemberInfos());
145 public PsiField
[] getNonNullFields() {
146 return memberInfosToFields(myNonNullPanel
.getTable().getSelectedMemberInfos());
149 private static PsiField
[] memberInfosToFields(MemberInfo
[] infos
) {
150 ArrayList
<PsiField
> list
= new ArrayList
<PsiField
>();
151 for (MemberInfo info
: infos
) {
152 list
.add((PsiField
)info
.getMember());
154 return list
.toArray(new PsiField
[list
.size()]);
157 protected void doNextAction() {
158 if (getCurrentStep() == myEqualsStepCode
&& myEqualsPanel
!= null) {
159 equalsFieldsSelected();
161 else if (getCurrentStep() == myHashcodeStepCode
&& myHashCodePanel
!= null) {
162 MemberInfo
[] selectedMemberInfos
= myHashCodePanel
.getTable().getSelectedMemberInfos();
163 updateNonNullMemberInfos(selectedMemberInfos
);
166 super.doNextAction();
170 protected void updateStep() {
172 final Component stepComponent
= getCurrentStepComponent();
173 if (stepComponent
instanceof MemberSelectionPanel
) {
174 ((MemberSelectionPanel
)stepComponent
).getTable().requestFocus();
178 protected String
getHelpID() {
179 return "editing.altInsert.equals";
182 private void equalsFieldsSelected() {
183 MemberInfo
[] selectedMemberInfos
= myEqualsPanel
.getTable().getSelectedMemberInfos();
184 updateHashCodeMemberInfos(selectedMemberInfos
);
185 updateNonNullMemberInfos(selectedMemberInfos
);
189 protected void doOKAction() {
190 if (myEqualsPanel
!= null) {
191 equalsFieldsSelected();
196 private HashMap
<PsiElement
, MemberInfo
> createFieldToMemberInfoMap(boolean checkedByDefault
) {
197 MemberInfo
[] memberInfos
= MemberInfo
.extractClassMembers(myClass
, MEMBER_INFO_FILTER
, false);
198 final HashMap
<PsiElement
, MemberInfo
> result
= new HashMap
<PsiElement
, MemberInfo
>();
199 for (MemberInfo memberInfo
: memberInfos
) {
200 memberInfo
.setChecked(checkedByDefault
);
201 result
.put(memberInfo
.getMember(), memberInfo
);
206 private void updateHashCodeMemberInfos(MemberInfo
[] equalsMemberInfos
) {
207 if (myHashCodePanel
== null) return;
208 MemberInfo
[] hashCodeFields
= new MemberInfo
[equalsMemberInfos
.length
];
210 for (int i
= 0; i
< equalsMemberInfos
.length
; i
++) {
211 hashCodeFields
[i
] = (MemberInfo
)myFieldsToHashCode
.get(equalsMemberInfos
[i
].getMember());
213 myHashCodePanel
.getTable().setMemberInfos(hashCodeFields
);
216 private void updateNonNullMemberInfos(MemberInfo
[] equalsMemberInfos
) {
217 final ArrayList
<MemberInfo
> list
= new ArrayList
<MemberInfo
>();
219 for (MemberInfo equalsMemberInfo
: equalsMemberInfos
) {
220 PsiField field
= (PsiField
)equalsMemberInfo
.getMember();
221 if (!(field
.getType() instanceof PsiPrimitiveType
)) {
222 list
.add(myFieldsToNonNull
.get(equalsMemberInfo
.getMember()));
225 myNonNullPanel
.getTable().setMemberInfos(list
.toArray(new MemberInfo
[list
.size()]));
228 private void updateStatus() {
229 boolean finishEnabled
= true;
230 boolean nextEnabled
= true;
231 if (myEqualsPanel
!= null & getCurrentStep() < myEqualsStepCode
) {
232 finishEnabled
= false;
235 if (getCurrentStep() == myTestBoxedStep
- 1) {
236 boolean anyNonBoxed
= false;
237 for (MemberInfo classField
: myClassFields
) {
238 if (classField
.isChecked()) {
239 PsiField field
= (PsiField
)classField
.getMember();
240 if (!(field
.getType() instanceof PsiPrimitiveType
)) {
246 nextEnabled
= anyNonBoxed
;
249 if (getCurrentStep() == myEqualsStepCode
) {
250 boolean anyChecked
= false;
251 for (MemberInfo classField
: myClassFields
) {
252 if (classField
.isChecked()) {
257 finishEnabled
&= anyChecked
;
258 nextEnabled
&= anyChecked
;
261 if (getCurrentStep() == myTestBoxedStep
) {
262 finishEnabled
= true;
266 getFinishButton().setEnabled(finishEnabled
);
267 getNextButton().setEnabled(nextEnabled
);
270 getRootPane().setDefaultButton(getFinishButton());
272 else if (nextEnabled
) {
273 getRootPane().setDefaultButton(getNextButton());
277 public JComponent
getPreferredFocusedComponent() {
278 final Component stepComponent
= getCurrentStepComponent();
279 if (stepComponent
instanceof MemberSelectionPanel
) {
280 return ((MemberSelectionPanel
)stepComponent
).getTable();
287 private class MyTableModelListener
implements TableModelListener
{
288 public void tableChanged(TableModelEvent e
) {
293 private static class InstanceofOptionStep
extends StepAdapter
{
294 private final JComponent myPanel
;
296 private InstanceofOptionStep() {
297 final JCheckBox checkbox
= new NonFocusableCheckBox(CodeInsightBundle
.message("generate.equals.hashcode.accept.sublcasses"));
298 checkbox
.setSelected(CodeInsightSettings
.getInstance().USE_INSTANCEOF_ON_EQUALS_PARAMETER
);
299 checkbox
.addActionListener(new ActionListener() {
300 public void actionPerformed(final ActionEvent e
) {
301 CodeInsightSettings
.getInstance().USE_INSTANCEOF_ON_EQUALS_PARAMETER
= checkbox
.isSelected();
305 myPanel
= new JPanel(new VerticalFlowLayout());
306 myPanel
.add(checkbox
);
307 myPanel
.add(new JLabel(CodeInsightBundle
.message("generate.equals.hashcode.accept.sublcasses.explanation")));
310 public JComponent
getComponent() {
315 public Icon
getIcon() {
320 private static class MyStep
extends StepAdapter
{
321 final MemberSelectionPanel myPanel
;
323 public MyStep(MemberSelectionPanel panel
) {
327 public Icon
getIcon() {
331 public JComponent
getComponent() {
337 private static class MyMemberInfoFilter
implements MemberInfo
.Filter
{
338 public boolean includeMember(PsiMember element
) {
339 return element
instanceof PsiField
&& !element
.hasModifierProperty(PsiModifier
.STATIC
);
344 private static class EqualsMemberInfoModel
implements MemberInfoModel
{
345 MemberInfoTooltipManager myTooltipManager
= new MemberInfoTooltipManager(new MemberInfoTooltipManager
.TooltipProvider() {
346 public String
getTooltip(MemberInfo memberInfo
) {
347 if (checkForProblems(memberInfo
) == OK
) return null;
348 if (!(memberInfo
.getMember() instanceof PsiField
)) return CodeInsightBundle
.message("generate.equals.hashcode.internal.error");
349 final PsiType type
= ((PsiField
)memberInfo
.getMember()).getType();
350 if (GenerateEqualsHelper
.isNestedArray(type
)) {
351 return CodeInsightBundle
.message("generate.equals.warning.equals.for.nested.arrays.not.supported");
353 if (GenerateEqualsHelper
.isArrayOfObjects(type
)) {
354 return CodeInsightBundle
.message("generate.equals.warning.generated.equals.could.be.incorrect");
360 public boolean isMemberEnabled(MemberInfo member
) {
361 if (!(member
.getMember() instanceof PsiField
)) return false;
362 final PsiType type
= ((PsiField
)member
.getMember()).getType();
363 return !GenerateEqualsHelper
.isNestedArray(type
);
366 public boolean isCheckedWhenDisabled(MemberInfo member
) {
370 public boolean isAbstractEnabled(MemberInfo member
) {
374 public boolean isAbstractWhenDisabled(MemberInfo member
) {
378 public Boolean
isFixedAbstract(MemberInfo member
) {
382 public int checkForProblems(@NotNull MemberInfo member
) {
383 if (!(member
.getMember() instanceof PsiField
)) return ERROR
;
384 final PsiType type
= ((PsiField
)member
.getMember()).getType();
385 if (GenerateEqualsHelper
.isNestedArray(type
)) return ERROR
;
386 if (GenerateEqualsHelper
.isArrayOfObjects(type
)) return WARNING
;
390 public void memberInfoChanged(MemberInfoChange event
) {
393 public String
getTooltipText(MemberInfo member
) {
394 return myTooltipManager
.getTooltip(member
);
398 private static class HashCodeMemberInfoModel
implements MemberInfoModel
{
399 private final MemberInfoTooltipManager myTooltipManager
= new MemberInfoTooltipManager(new MemberInfoTooltipManager
.TooltipProvider() {
400 public String
getTooltip(MemberInfo memberInfo
) {
401 if (isMemberEnabled(memberInfo
)) return null;
402 if (!(memberInfo
.getMember() instanceof PsiField
)) return CodeInsightBundle
.message("generate.equals.hashcode.internal.error");
403 final PsiType type
= ((PsiField
)memberInfo
.getMember()).getType();
404 if (!(type
instanceof PsiArrayType
)) return null;
405 return CodeInsightBundle
.message("generate.equals.hashcode.warning.hashcode.for.arrays.is.not.supported");
409 public boolean isMemberEnabled(MemberInfo member
) {
410 final PsiMember psiMember
= member
.getMember();
411 return psiMember
instanceof PsiField
;
414 public boolean isCheckedWhenDisabled(MemberInfo member
) {
418 public boolean isAbstractEnabled(MemberInfo member
) {
422 public boolean isAbstractWhenDisabled(MemberInfo member
) {
426 public Boolean
isFixedAbstract(MemberInfo member
) {
430 public int checkForProblems(@NotNull MemberInfo member
) {
434 public void memberInfoChanged(MemberInfoChange event
) {
437 public String
getTooltipText(MemberInfo member
) {
438 return myTooltipManager
.getTooltip(member
);