generate equals/hashCode: support deep arrays equals for java > 1.5 (IDEA-131533)

This commit is contained in:
Anna Kozlova
2014-12-24 20:26:36 +01:00
parent 6f12108208
commit bb51fb1c63
3 changed files with 48 additions and 31 deletions
@@ -18,9 +18,9 @@ package com.intellij.codeInsight.generation;
import com.intellij.codeInsight.CodeInsightBundle;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.module.ModuleUtilCore;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.projectRoots.JavaSdkVersion;
import com.intellij.openapi.projectRoots.JavaVersionService;
import com.intellij.psi.*;
import com.intellij.psi.codeStyle.*;
import com.intellij.psi.search.GlobalSearchScope;
@@ -234,6 +234,9 @@ public class GenerateEqualsHelper implements Runnable {
@NonNls private static final MessageFormat ARRAY_COMPARER_MF =
new MessageFormat("if(!java.util.Arrays.equals({1}, {0}.{1})) return false;\n");
@NonNls private static final MessageFormat ARRAY_DEEP_COMPARER_MF =
new MessageFormat("if(!java.util.Arrays.deepEquals({1}, {0}.{1})) return false;\n");
@NonNls private static final MessageFormat FIELD_COMPARER_MF =
new MessageFormat("if({1}!=null ? !{1}.equals({0}.{1}) : {0}.{1}!= null)return false;\n");
@NonNls private static final MessageFormat NON_NULL_FIELD_COMPARER_MF = new MessageFormat("if(!{1}.equals({0}.{1}))return false;\n");
@@ -244,9 +247,14 @@ public class GenerateEqualsHelper implements Runnable {
private void addArrayEquals(StringBuffer buffer, PsiField field) {
final PsiType fieldType = field.getType();
if (isNestedArray(fieldType)) {
buffer.append(" ");
buffer.append(CodeInsightBundle.message("generate.equals.compare.nested.arrays.comment", field.getName()));
buffer.append("\n");
if (JavaVersionService.getInstance().isAtLeast(field, JavaSdkVersion.JDK_1_5)) {
ARRAY_DEEP_COMPARER_MF.format(getComparerFormatParameters(field), buffer, null);
}
else {
buffer.append(" ");
buffer.append(CodeInsightBundle.message("generate.equals.compare.nested.arrays.comment", field.getName()));
buffer.append("\n");
}
return;
}
if (isArrayOfObjects(fieldType)) {
@@ -474,7 +482,14 @@ public class GenerateEqualsHelper implements Runnable {
}
private static void adjustHashCodeToArrays(@NonNls StringBuilder buffer, final PsiField field, final String name) {
if (field.getType() instanceof PsiArrayType && hasArraysHashCode(field)) {
final PsiType fieldType = field.getType();
if (fieldType instanceof PsiArrayType &&
JavaVersionService.getInstance().isAtLeast(field, JavaSdkVersion.JDK_1_5)) {
if (isNestedArray(fieldType)) {
buffer.append(" ");
buffer.append("// Probably incorrect - hashCode for high dimension arrays with Arrays.hashCode");
buffer.append("\n");
}
buffer.append("java.util.Arrays.hashCode(");
buffer.append(name);
buffer.append(")");
@@ -485,16 +500,6 @@ public class GenerateEqualsHelper implements Runnable {
}
}
private static boolean hasArraysHashCode(final PsiField field) {
// the method was added in JDK 1.5 - check for actual method presence rather than language level
Module module = ModuleUtilCore.findModuleForPsiElement(field);
if (module == null) return false;
PsiClass arraysClass = JavaPsiFacade.getInstance(field.getProject()).findClass("java.util.Arrays", module.getModuleWithLibrariesScope());
if (arraysClass == null) return false;
final PsiMethod[] methods = arraysClass.findMethodsByName("hashCode", false);
return methods.length > 0;
}
@SuppressWarnings("HardCodedStringLiteral")
private void addSuperHashCode(StringBuilder buffer) {
if (mySuperHasHashCode) {
@@ -22,8 +22,11 @@ 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.projectRoots.JavaSdkVersion;
import com.intellij.openapi.projectRoots.JavaVersionService;
import com.intellij.openapi.ui.VerticalFlowLayout;
import com.intellij.psi.*;
import com.intellij.psi.util.PsiUtil;
import com.intellij.refactoring.classMembers.AbstractMemberInfoModel;
import com.intellij.refactoring.classMembers.MemberInfoBase;
import com.intellij.refactoring.classMembers.MemberInfoTooltipManager;
@@ -263,12 +266,15 @@ public class GenerateEqualsWizard extends AbstractGenerateEqualsWizard<PsiClass,
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");
final PsiField field = (PsiField)memberInfo.getMember();
if (!JavaVersionService.getInstance().isAtLeast(field, JavaSdkVersion.JDK_1_5)) {
final PsiType type = field.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;
}
@@ -277,16 +283,20 @@ public class GenerateEqualsWizard extends AbstractGenerateEqualsWizard<PsiClass,
@Override
public boolean isMemberEnabled(MemberInfo member) {
if (!(member.getMember() instanceof PsiField)) return false;
final PsiType type = ((PsiField)member.getMember()).getType();
return !GenerateEqualsHelper.isNestedArray(type);
final PsiField field = (PsiField)member.getMember();
final PsiType type = field.getType();
return JavaVersionService.getInstance().isAtLeast(field, JavaSdkVersion.JDK_1_5) || !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;
final PsiField field = (PsiField)member.getMember();
final PsiType type = field.getType();
if (!JavaVersionService.getInstance().isAtLeast(field, JavaSdkVersion.JDK_1_5)) {
if (GenerateEqualsHelper.isNestedArray(type)) return ERROR;
if (GenerateEqualsHelper.isArrayOfObjects(type)) return WARNING;
}
return OK;
}
@@ -302,8 +312,9 @@ public class GenerateEqualsWizard extends AbstractGenerateEqualsWizard<PsiClass,
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;
final PsiField field = (PsiField)memberInfo.getMember();
final PsiType type = field.getType();
if (!(type instanceof PsiArrayType) || JavaVersionService.getInstance().isAtLeast(field, JavaSdkVersion.JDK_1_5)) return null;
return CodeInsightBundle.message("generate.equals.hashcode.warning.hashcode.for.arrays.is.not.supported");
}
});
@@ -11,7 +11,7 @@ class Test {
final Test test = (Test) o;
// Compare nested arrays - values of myIIs here
if (!Arrays.deepEquals(myIIs, test.myIIs)) return false;
if (!Arrays.equals(myIs, test.myIs)) return false;
// Probably incorrect - comparing Object[] arrays with Arrays.equals
if (!Arrays.equals(myOs, test.myOs)) return false;
@@ -21,7 +21,8 @@ class Test {
public int hashCode() {
int result = myOs != null ? Arrays.hashCode(myOs) : 0;
result = 31 * result + (myIIs != null ? Arrays.hashCode(myIIs) : 0);
result = 31 * result + (myIIs != null ? // Probably incorrect - hashCode for high dimension arrays with Arrays.hashCode
Arrays.hashCode(myIIs) : 0);
result = 31 * result + (myIs != null ? Arrays.hashCode(myIs) : 0);
return result;
}