blob: 035a469e500cbda6c3b31de5603639f8fcc06e36 [file] [log] [blame]
/*
* Copyright 2000-2011 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.intellij.codeInsight.generation.ui;
import com.intellij.codeInsight.CodeInsightBundle;
import com.intellij.codeInsight.CodeInsightSettings;
import com.intellij.codeInsight.NullableNotNullManager;
import com.intellij.codeInsight.generation.GenerateEqualsHelper;
import com.intellij.ide.wizard.StepAdapter;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.ui.VerticalFlowLayout;
import com.intellij.psi.*;
import com.intellij.refactoring.classMembers.AbstractMemberInfoModel;
import com.intellij.refactoring.classMembers.MemberInfoBase;
import com.intellij.refactoring.classMembers.MemberInfoTooltipManager;
import com.intellij.refactoring.ui.AbstractMemberSelectionPanel;
import com.intellij.refactoring.ui.MemberSelectionPanel;
import com.intellij.refactoring.util.classMembers.MemberInfo;
import com.intellij.ui.NonFocusableCheckBox;
import com.intellij.util.containers.HashMap;
import org.jetbrains.annotations.NotNull;
import javax.swing.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.*;
/**
* @author dsl
*/
public class GenerateEqualsWizard extends AbstractGenerateEqualsWizard<PsiClass, PsiMember, MemberInfo> {
private static final Logger LOG = Logger.getInstance("#com.intellij.codeInsight.generation.ui.GenerateEqualsWizard");
private static final MyMemberInfoFilter MEMBER_INFO_FILTER = new MyMemberInfoFilter();
public static class JavaGenerateEqualsWizardBuilder extends AbstractGenerateEqualsWizard.Builder<PsiClass, PsiMember, MemberInfo> {
private final PsiClass myClass;
private final MemberSelectionPanel myEqualsPanel;
private final MemberSelectionPanel myHashCodePanel;
private final MemberSelectionPanel myNonNullPanel;
private final HashMap<PsiMember, MemberInfo> myFieldsToHashCode;
private final HashMap<PsiMember, MemberInfo> myFieldsToNonNull;
private final List<MemberInfo> myClassFields;
private JavaGenerateEqualsWizardBuilder(PsiClass aClass, boolean needEquals, boolean needHashCode) {
LOG.assertTrue(needEquals || needHashCode);
myClass = aClass;
myClassFields = MemberInfo.extractClassMembers(myClass, MEMBER_INFO_FILTER, false);
for (MemberInfo myClassField : myClassFields) {
myClassField.setChecked(true);
}
if (needEquals) {
myEqualsPanel = new MemberSelectionPanel(CodeInsightBundle.message("generate.equals.hashcode.equals.fields.chooser.title"),
myClassFields, null);
myEqualsPanel.getTable().setMemberInfoModel(new EqualsMemberInfoModel());
}
else {
myEqualsPanel = null;
}
if (needHashCode) {
final List<MemberInfo> hashCodeMemberInfos;
if (needEquals) {
myFieldsToHashCode = createFieldToMemberInfoMap(true);
hashCodeMemberInfos = Collections.emptyList();
}
else {
hashCodeMemberInfos = myClassFields;
myFieldsToHashCode = null;
}
myHashCodePanel = new MemberSelectionPanel(CodeInsightBundle.message("generate.equals.hashcode.hashcode.fields.chooser.title"), hashCodeMemberInfos, null);
myHashCodePanel.getTable().setMemberInfoModel(new HashCodeMemberInfoModel());
if (needEquals) {
updateHashCodeMemberInfos(myClassFields);
}
}
else {
myHashCodePanel = null;
myFieldsToHashCode = null;
}
myNonNullPanel = new MemberSelectionPanel(CodeInsightBundle.message("generate.equals.hashcode.non.null.fields.chooser.title"), Collections.<MemberInfo>emptyList(), null);
myFieldsToNonNull = createFieldToMemberInfoMap(false);
for (final Map.Entry<PsiMember, MemberInfo> entry : myFieldsToNonNull.entrySet()) {
entry.getValue().setChecked(NullableNotNullManager.isNotNull(entry.getKey()));
}
}
@Override
protected List<MemberInfo> getClassFields() {
return myClassFields;
}
@Override
protected HashMap<PsiMember, MemberInfo> getFieldsToHashCode() {
return myFieldsToHashCode;
}
@Override
protected HashMap<PsiMember, MemberInfo> getFieldsToNonNull() {
return myFieldsToNonNull;
}
@Override
protected AbstractMemberSelectionPanel<PsiMember, MemberInfo> getEqualsPanel() {
return myEqualsPanel;
}
@Override
protected AbstractMemberSelectionPanel<PsiMember, MemberInfo> getHashCodePanel() {
return myHashCodePanel;
}
@Override
protected AbstractMemberSelectionPanel<PsiMember, MemberInfo> getNonNullPanel() {
return myNonNullPanel;
}
@Override
protected PsiClass getPsiClass() {
return myClass;
}
@Override
protected void updateHashCodeMemberInfos(Collection<MemberInfo> equalsMemberInfos) {
if (myHashCodePanel == null) return;
List<MemberInfo> hashCodeFields = new ArrayList<MemberInfo>();
for (MemberInfo equalsMemberInfo : equalsMemberInfos) {
hashCodeFields.add(myFieldsToHashCode.get(equalsMemberInfo.getMember()));
}
myHashCodePanel.getTable().setMemberInfos(hashCodeFields);
}
@Override
protected void updateNonNullMemberInfos(Collection<MemberInfo> equalsMemberInfos) {
final ArrayList<MemberInfo> list = new ArrayList<MemberInfo>();
for (MemberInfo equalsMemberInfo : equalsMemberInfos) {
PsiField field = (PsiField)equalsMemberInfo.getMember();
if (!(field.getType() instanceof PsiPrimitiveType)) {
list.add(myFieldsToNonNull.get(equalsMemberInfo.getMember()));
}
}
myNonNullPanel.getTable().setMemberInfos(list);
}
private HashMap<PsiMember, MemberInfo> createFieldToMemberInfoMap(boolean checkedByDefault) {
Collection<MemberInfo> memberInfos = MemberInfo.extractClassMembers(myClass, MEMBER_INFO_FILTER, false);
final HashMap<PsiMember, MemberInfo> result = new HashMap<PsiMember, MemberInfo>();
for (MemberInfo memberInfo : memberInfos) {
memberInfo.setChecked(checkedByDefault);
result.put(memberInfo.getMember(), memberInfo);
}
return result;
}
}
public GenerateEqualsWizard(Project project, PsiClass aClass, boolean needEquals, boolean needHashCode) {
super(project, new JavaGenerateEqualsWizardBuilder(aClass, needEquals, needHashCode));
}
public PsiField[] getEqualsFields() {
if (myEqualsPanel != null) {
return memberInfosToFields(myEqualsPanel.getTable().getSelectedMemberInfos());
}
else {
return null;
}
}
public PsiField[] getHashCodeFields() {
if (myHashCodePanel != null) {
return memberInfosToFields(myHashCodePanel.getTable().getSelectedMemberInfos());
}
else {
return null;
}
}
public PsiField[] getNonNullFields() {
return memberInfosToFields(myNonNullPanel.getTable().getSelectedMemberInfos());
}
private static PsiField[] memberInfosToFields(Collection<MemberInfo> infos) {
ArrayList<PsiField> list = new ArrayList<PsiField>();
for (MemberInfo info : infos) {
list.add((PsiField)info.getMember());
}
return list.toArray(new PsiField[list.size()]);
}
@Override
protected String getHelpID() {
return "editing.altInsert.equals";
}
private void equalsFieldsSelected() {
Collection<MemberInfo> selectedMemberInfos = myEqualsPanel.getTable().getSelectedMemberInfos();
updateHashCodeMemberInfos(selectedMemberInfos);
updateNonNullMemberInfos(selectedMemberInfos);
}
@Override
protected void doOKAction() {
if (myEqualsPanel != null) {
equalsFieldsSelected();
}
super.doOKAction();
}
@Override
protected int getNextStep(int step) {
if (step + 1 == getNonNullStepCode()) {
for (MemberInfo classField : myClassFields) {
if (classField.isChecked()) {
PsiField field = (PsiField)classField.getMember();
if (!(field.getType() instanceof PsiPrimitiveType)) {
return getNonNullStepCode();
}
}
}
return step;
}
return super.getNextStep(step);
}
@Override
protected void addSteps() {
if (myEqualsPanel != null) {
addStep(new InstanceofOptionStep(myClass.hasModifierProperty(PsiModifier.FINAL)));
}
super.addSteps();
}
private static class MyMemberInfoFilter implements MemberInfoBase.Filter<PsiMember> {
@Override
public boolean includeMember(PsiMember element) {
return element instanceof PsiField && !element.hasModifierProperty(PsiModifier.STATIC);
}
}
private static class EqualsMemberInfoModel extends AbstractMemberInfoModel<PsiMember, MemberInfo> {
MemberInfoTooltipManager<PsiMember, MemberInfo> myTooltipManager =
new MemberInfoTooltipManager<PsiMember, MemberInfo>(new MemberInfoTooltipManager.TooltipProvider<PsiMember, MemberInfo>() {
@Override
public String getTooltip(MemberInfo memberInfo) {
if (checkForProblems(memberInfo) == OK) return null;
if (!(memberInfo.getMember() instanceof PsiField)) return CodeInsightBundle.message("generate.equals.hashcode.internal.error");
final PsiType type = ((PsiField)memberInfo.getMember()).getType();
if (GenerateEqualsHelper.isNestedArray(type)) {
return CodeInsightBundle .message("generate.equals.warning.equals.for.nested.arrays.not.supported");
}
if (GenerateEqualsHelper.isArrayOfObjects(type)) {
return CodeInsightBundle.message("generate.equals.warning.generated.equals.could.be.incorrect");
}
return null;
}
});
@Override
public boolean isMemberEnabled(MemberInfo member) {
if (!(member.getMember() instanceof PsiField)) return false;
final PsiType type = ((PsiField)member.getMember()).getType();
return !GenerateEqualsHelper.isNestedArray(type);
}
@Override
public int checkForProblems(@NotNull MemberInfo member) {
if (!(member.getMember() instanceof PsiField)) return ERROR;
final PsiType type = ((PsiField)member.getMember()).getType();
if (GenerateEqualsHelper.isNestedArray(type)) return ERROR;
if (GenerateEqualsHelper.isArrayOfObjects(type)) return WARNING;
return OK;
}
@Override
public String getTooltipText(MemberInfo member) {
return myTooltipManager.getTooltip(member);
}
}
private static class HashCodeMemberInfoModel extends AbstractMemberInfoModel<PsiMember, MemberInfo> {
private final MemberInfoTooltipManager<PsiMember, MemberInfo> myTooltipManager = new MemberInfoTooltipManager<PsiMember, MemberInfo>(new MemberInfoTooltipManager.TooltipProvider<PsiMember, MemberInfo>() {
@Override
public String getTooltip(MemberInfo memberInfo) {
if (isMemberEnabled(memberInfo)) return null;
if (!(memberInfo.getMember() instanceof PsiField)) return CodeInsightBundle.message("generate.equals.hashcode.internal.error");
final PsiType type = ((PsiField)memberInfo.getMember()).getType();
if (!(type instanceof PsiArrayType)) return null;
return CodeInsightBundle.message("generate.equals.hashcode.warning.hashcode.for.arrays.is.not.supported");
}
});
@Override
public boolean isMemberEnabled(MemberInfo member) {
final PsiMember psiMember = member.getMember();
return psiMember instanceof PsiField;
}
@Override
public String getTooltipText(MemberInfo member) {
return myTooltipManager.getTooltip(member);
}
}
private static class InstanceofOptionStep extends StepAdapter {
private final JComponent myPanel;
private InstanceofOptionStep(boolean isFinal) {
final JCheckBox checkbox = new NonFocusableCheckBox(CodeInsightBundle.message("generate.equals.hashcode.accept.sublcasses"));
checkbox.setSelected(!isFinal && CodeInsightSettings.getInstance().USE_INSTANCEOF_ON_EQUALS_PARAMETER);
checkbox.setEnabled(!isFinal);
checkbox.addActionListener(new ActionListener() {
public void actionPerformed(@NotNull final ActionEvent M) {
CodeInsightSettings.getInstance().USE_INSTANCEOF_ON_EQUALS_PARAMETER = checkbox.isSelected();
}
});
myPanel = new JPanel(new VerticalFlowLayout());
myPanel.add(checkbox);
myPanel.add(new JLabel(CodeInsightBundle.message("generate.equals.hashcode.accept.sublcasses.explanation")));
}
@Override
public JComponent getComponent() {
return myPanel;
}
}
}