Bug 424214 - Generation of equals and hashcode with java 7
Objects.equals and Objects.hashcode
Implemented Java 7 generator. Fixed Object[], Serializable[] and
Cloneable[] cases for all implementations.
Change-Id: I1b7258d69f3925b77b2f72f017afc64da7a0cac0
Signed-off-by: Pierre-Yves B. <pyvesdev@gmail.com>
Also-by: Ivan Konstantinov <threesixty@abv.bg>
diff --git a/org.eclipse.jdt.ui.tests/ui/org/eclipse/jdt/ui/tests/core/source/GenerateHashCodeEqualsTest.java b/org.eclipse.jdt.ui.tests/ui/org/eclipse/jdt/ui/tests/core/source/GenerateHashCodeEqualsTest.java
index d450dbe..670b0d6 100644
--- a/org.eclipse.jdt.ui.tests/ui/org/eclipse/jdt/ui/tests/core/source/GenerateHashCodeEqualsTest.java
+++ b/org.eclipse.jdt.ui.tests/ui/org/eclipse/jdt/ui/tests/core/source/GenerateHashCodeEqualsTest.java
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2000, 2016 IBM Corporation and others.
+ * Copyright (c) 2000, 2018 IBM Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
@@ -7,6 +7,7 @@
*
* Contributors:
* IBM Corporation - initial API and implementation
+ * Pierre-Yves B. <pyvesdev@gmail.com> - Generation of equals and hashcode with java 7 Objects.equals and Objects.hashcode - https://bugs.eclipse.org/424214
*******************************************************************************/
package org.eclipse.jdt.ui.tests.core.source;
@@ -64,7 +65,7 @@
return setUpTest(new TestSuite(THIS));
}
- public void runOperation(IType type, IField[] fields, IJavaElement insertBefore, boolean createComments, boolean useInstanceof, boolean useBlocks, boolean force) throws CoreException {
+ public void runOperation(IType type, IField[] fields, IJavaElement insertBefore, boolean createComments, boolean useInstanceof, boolean useJ7HashEquals, boolean useBlocks, boolean force) throws CoreException {
RefactoringASTParser parser= new RefactoringASTParser(IASTSharedValues.SHARED_AST_LEVEL);
CompilationUnit unit= parser.parse(type.getCompilationUnit(), true);
@@ -80,14 +81,18 @@
AbstractTypeDeclaration decl= ASTNodeSearchUtil.getAbstractTypeDeclarationNode(type, unit);
ITypeBinding binding= decl.resolveBinding();
- GenerateHashCodeEqualsOperation op= new GenerateHashCodeEqualsOperation(binding, fKeys, unit, insertBefore, fSettings, useInstanceof, force, true, true);
+ GenerateHashCodeEqualsOperation op= new GenerateHashCodeEqualsOperation(binding, fKeys, unit, insertBefore, fSettings, useInstanceof, useJ7HashEquals, force, true, true);
op.setUseBlocksForThen(useBlocks);
op.run(new NullProgressMonitor());
JavaModelUtil.reconcile(type.getCompilationUnit());
}
public void runOperation(IType type, IField[] fields, boolean useInstanceof, boolean force) throws CoreException {
- runOperation(type, fields, null, true, useInstanceof, false, force);
+ runOperation(type, fields, null, true, useInstanceof, false, false, force);
+ }
+
+ public void runJ7Operation(IType type, IField[] fields, boolean useInstanceof) throws CoreException {
+ runOperation(type, fields, null, true, useInstanceof, true, false, false);
}
private IField[] getFields(IType type, String[] fieldNames) {
@@ -493,7 +498,7 @@
"", true, null);
IField[] fields= getFields(a.getType("A"), new String[] {"someInt" });
- runOperation(a.getType("A"), fields, a.getType("A").getMethod("bar", new String[0]), true, false, false, false);
+ runOperation(a.getType("A"), fields, a.getType("A").getMethod("bar", new String[0]), true, false, false, false, false);
String expected= "package p;\r\n" +
"\r\n" +
@@ -1056,6 +1061,775 @@
}
/**
+ * Test with J7+ Objects.hash and Objects.equals method calls
+ * Using non-array instance variables and Enum
+ *
+ * @throws Exception
+ */
+ public void testHashCodeEqualsIn17() throws Exception {
+
+ ICompilationUnit a= fPackageP.createCompilationUnit("A.java", "package p;\r\n" +
+ "import java.util.List;\r\n" +
+ "import java.lang.annotation.ElementType;\r\n" +
+ "public class A {\r\n" +
+ " boolean aBool;\r\n" +
+ " byte aByte;\r\n" +
+ " char aChar;\r\n" +
+ " int anInt;\r\n" +
+ " double aDouble;\r\n" +
+ " float aFloat;\r\n" +
+ " long aLong;\r\n" +
+ " String aString;\r\n" +
+ " List<String> aListOfStrings;\r\n" +
+ " ElementType anEnum;\r\n" +
+ "\r\n" +
+ "}\r\n" +
+ "", true, null);
+
+ IField[] fields= getFields(a.getType("A"), new String[] { "aBool", "aByte", "aChar", "anInt", "aDouble", "aFloat", "aLong", "aString", "aListOfStrings", "anEnum" });
+ runJ7Operation(a.getType("A"), fields, false);
+
+ String expected= "package p;\r\n" +
+ "import java.util.List;\r\n" +
+ "import java.util.Objects;\r\n" +
+ "import java.lang.annotation.ElementType;\r\n" +
+ "public class A {\r\n" +
+ " boolean aBool;\r\n" +
+ " byte aByte;\r\n" +
+ " char aChar;\r\n" +
+ " int anInt;\r\n" +
+ " double aDouble;\r\n" +
+ " float aFloat;\r\n" +
+ " long aLong;\r\n" +
+ " String aString;\r\n" +
+ " List<String> aListOfStrings;\r\n" +
+ " ElementType anEnum;\r\n" +
+ " /* (non-Javadoc)\r\n" +
+ " * @see java.lang.Object#hashCode()\r\n" +
+ " */\r\n" +
+ " @Override\r\n" +
+ " public int hashCode() {\r\n" +
+ " return Objects.hash(aBool, aByte, aChar, anInt, aDouble, aFloat, aLong, aString, aListOfStrings, anEnum);\r\n" +
+ " }\r\n" +
+ " /* (non-Javadoc)\r\n" +
+ " * @see java.lang.Object#equals(java.lang.Object)\r\n" +
+ " */\r\n" +
+ " @Override\r\n" +
+ " public boolean equals(Object obj) {\r\n" +
+ " if (this == obj)\r\n" +
+ " return true;\r\n" +
+ " if (obj == null)\r\n" +
+ " return false;\r\n" +
+ " if (getClass() != obj.getClass())\r\n" +
+ " return false;\r\n" +
+ " A other = (A) obj;\r\n" +
+ " return aBool == other.aBool && aByte == other.aByte && aChar == other.aChar && anInt == other.anInt && Double.doubleToLongBits(aDouble) == Double.doubleToLongBits(other.aDouble) && Float.floatToIntBits(aFloat) == Float.floatToIntBits(other.aFloat) && aLong == other.aLong && Objects.equals(aString, other.aString) && Objects.equals(aListOfStrings, other.aListOfStrings) && anEnum == other.anEnum;\r\n"
+ +
+ " }\r\n" +
+ "\r\n" +
+ "}\r\n" +
+ "";
+
+ compareSource(expected, a.getSource());
+ }
+
+ /**
+ * Test with J7+ Objects.hash and Objects.equals method calls
+ * Using unique non-array instance variables
+ *
+ * @throws Exception
+ */
+ public void testHashCodeEqualsUniqueFieldIn17() throws Exception {
+
+ ICompilationUnit a= fPackageP.createCompilationUnit("A.java", "package p;\r\n" +
+ "public class A {\r\n" +
+ " String aString;\r\n" +
+ "\r\n" +
+ "}\r\n" +
+ "", true, null);
+
+ IField[] fields= getFields(a.getType("A"), new String[] { "aString" });
+ runJ7Operation(a.getType("A"), fields, false);
+
+ String expected= "package p;\r\n" +
+ "\r\n" +
+ "import java.util.Objects;\r\n" +
+ "\r\n" +
+ "public class A {\r\n" +
+ " String aString;\r\n" +
+ "\r\n" +
+ " /* (non-Javadoc)\r\n" +
+ " * @see java.lang.Object#hashCode()\r\n" +
+ " */\r\n" +
+ " @Override\r\n" +
+ " public int hashCode() {\r\n" +
+ " return Objects.hash(aString);\r\n" +
+ " }\r\n" +
+ "\r\n" +
+ " /* (non-Javadoc)\r\n" +
+ " * @see java.lang.Object#equals(java.lang.Object)\r\n" +
+ " */\r\n" +
+ " @Override\r\n" +
+ " public boolean equals(Object obj) {\r\n" +
+ " if (this == obj)\r\n" +
+ " return true;\r\n" +
+ " if (obj == null)\r\n" +
+ " return false;\r\n" +
+ " if (getClass() != obj.getClass())\r\n" +
+ " return false;\r\n" +
+ " A other = (A) obj;\r\n" +
+ " return Objects.equals(aString, other.aString);\r\n" +
+ " }\r\n" +
+ "\r\n" +
+ "}\r\n" +
+ "";
+
+ compareSource(expected, a.getSource());
+ }
+
+ /**
+ * Test with J7+ Objects.hash and Objects.equals method calls
+ * Using non-array instance variables with 'instanceof' comparison
+ *
+ * @throws Exception
+ */
+ public void testHashCodeEqualsInstanceOfIn17() throws Exception {
+
+ ICompilationUnit a= fPackageP.createCompilationUnit("A.java", "package p;\r\n" +
+ "import java.util.List;\r\n" +
+ "public class A {\r\n" +
+ " boolean aBool;\r\n" +
+ " byte aByte;\r\n" +
+ " char aChar;\r\n" +
+ " int anInt;\r\n" +
+ " double aDouble;\r\n" +
+ " float aFloat;\r\n" +
+ " long aLong;\r\n" +
+ " String aString;\r\n" +
+ " List<String> aListOfStrings;\r\n" +
+ "\r\n" +
+ "}\r\n" +
+ "", true, null);
+
+ IField[] fields= getFields(a.getType("A"), new String[] { "aBool", "aByte", "aChar", "anInt", "aDouble", "aFloat", "aLong", "aString", "aListOfStrings" });
+ runJ7Operation(a.getType("A"), fields, true);
+
+ String expected= "package p;\r\n" +
+ "import java.util.List;\r\n" +
+ "import java.util.Objects;\r\n" +
+ "public class A {\r\n" +
+ " boolean aBool;\r\n" +
+ " byte aByte;\r\n" +
+ " char aChar;\r\n" +
+ " int anInt;\r\n" +
+ " double aDouble;\r\n" +
+ " float aFloat;\r\n" +
+ " long aLong;\r\n" +
+ " String aString;\r\n" +
+ " List<String> aListOfStrings;\r\n" +
+ " /* (non-Javadoc)\r\n" +
+ " * @see java.lang.Object#hashCode()\r\n" +
+ " */\r\n" +
+ " @Override\r\n" +
+ " public int hashCode() {\r\n" +
+ " return Objects.hash(aBool, aByte, aChar, anInt, aDouble, aFloat, aLong, aString, aListOfStrings);\r\n" +
+ " }\r\n" +
+ " /* (non-Javadoc)\r\n" +
+ " * @see java.lang.Object#equals(java.lang.Object)\r\n" +
+ " */\r\n" +
+ " @Override\r\n" +
+ " public boolean equals(Object obj) {\r\n" +
+ " if (this == obj)\r\n" +
+ " return true;\r\n" +
+ " if (obj == null)\r\n" +
+ " return false;\r\n" +
+ " if (!(obj instanceof A))\r\n" +
+ " return false;\r\n" +
+ " A other = (A) obj;\r\n" +
+ " return aBool == other.aBool && aByte == other.aByte && aChar == other.aChar && anInt == other.anInt && Double.doubleToLongBits(aDouble) == Double.doubleToLongBits(other.aDouble) && Float.floatToIntBits(aFloat) == Float.floatToIntBits(other.aFloat) && aLong == other.aLong && Objects.equals(aString, other.aString) && Objects.equals(aListOfStrings, other.aListOfStrings);\r\n"
+ +
+ " }\r\n" +
+ "\r\n" +
+ "}\r\n" +
+ "";
+
+ compareSource(expected, a.getSource());
+ }
+
+ /**
+ * Test with J7+ Objects.hash and Objects.equals method calls
+ * Using 1-dim array amongst other instance variables
+ *
+ * @throws Exception
+ */
+ public void testHashCodeEqualsArrayIn17() throws Exception {
+
+ ICompilationUnit a= fPackageP.createCompilationUnit("A.java", "package p;\r\n" +
+ "import java.util.List;\r\n" +
+ "public class A {\r\n" +
+ " boolean aBool;\r\n" +
+ " byte aByte;\r\n" +
+ " char aChar;\r\n" +
+ " int anInt;\r\n" +
+ " double aDouble;\r\n" +
+ " float aFloat;\r\n" +
+ " long aLong;\r\n" +
+ " String aString;\r\n" +
+ " List<String> aListOfStrings;\r\n" +
+ " int[] anArrayOfInts;\r\n" +
+ "\r\n" +
+ "}\r\n" +
+ "", true, null);
+
+ IField[] fields= getFields(a.getType("A"), new String[] { "aBool", "aByte", "aChar", "anInt", "aDouble", "aFloat", "aLong", "aString", "aListOfStrings", "anArrayOfInts" });
+ runJ7Operation(a.getType("A"), fields, false);
+
+ String expected= "package p;\r\n" +
+ "import java.util.Arrays;\r\n" +
+ "import java.util.List;\r\n" +
+ "import java.util.Objects;\r\n" +
+ "public class A {\r\n" +
+ " boolean aBool;\r\n" +
+ " byte aByte;\r\n" +
+ " char aChar;\r\n" +
+ " int anInt;\r\n" +
+ " double aDouble;\r\n" +
+ " float aFloat;\r\n" +
+ " long aLong;\r\n" +
+ " String aString;\r\n" +
+ " List<String> aListOfStrings;\r\n" +
+ " int[] anArrayOfInts;\r\n" +
+ " /* (non-Javadoc)\r\n" +
+ " * @see java.lang.Object#hashCode()\r\n" +
+ " */\r\n" +
+ " @Override\r\n" +
+ " public int hashCode() {\r\n" +
+ " final int prime = 31;\r\n" +
+ " int result = 1;\r\n" +
+ " result = prime * result + Objects.hash(aBool, aByte, aChar, anInt, aDouble, aFloat, aLong, aString, aListOfStrings);\r\n" +
+ " result = prime * result + Arrays.hashCode(anArrayOfInts);\r\n" +
+ " return result;\r\n" +
+ " }\r\n" +
+ " /* (non-Javadoc)\r\n" +
+ " * @see java.lang.Object#equals(java.lang.Object)\r\n" +
+ " */\r\n" +
+ " @Override\r\n" +
+ " public boolean equals(Object obj) {\r\n" +
+ " if (this == obj)\r\n" +
+ " return true;\r\n" +
+ " if (obj == null)\r\n" +
+ " return false;\r\n" +
+ " if (getClass() != obj.getClass())\r\n" +
+ " return false;\r\n" +
+ " A other = (A) obj;\r\n" +
+ " return aBool == other.aBool && aByte == other.aByte && aChar == other.aChar && anInt == other.anInt && Double.doubleToLongBits(aDouble) == Double.doubleToLongBits(other.aDouble) && Float.floatToIntBits(aFloat) == Float.floatToIntBits(other.aFloat) && aLong == other.aLong && Objects.equals(aString, other.aString) && Objects.equals(aListOfStrings, other.aListOfStrings) && Arrays.equals(anArrayOfInts, other.anArrayOfInts);\r\n"
+ +
+ " }\r\n" +
+ "\r\n" +
+ "}\r\n" +
+ "";
+
+ compareSource(expected, a.getSource());
+ }
+
+ /**
+ * Test with J7+ Objects.hash and Objects.equals method calls
+ * Using 1-dim Cloneable array amongst other instance variables
+ * @throws Exception
+ */
+ public void testHashCodeEqualsCloneableArrayIn17() throws Exception {
+
+ ICompilationUnit a= fPackageP.createCompilationUnit("A.java", "package p;\r\n" +
+ "import java.util.List;\r\n" +
+ "public class A {\r\n" +
+ " boolean aBool;\r\n" +
+ " byte aByte;\r\n" +
+ " char aChar;\r\n" +
+ " int anInt;\r\n" +
+ " double aDouble;\r\n" +
+ " float aFloat;\r\n" +
+ " long aLong;\r\n" +
+ " String aString;\r\n" +
+ " List<String> aListOfStrings;\r\n" +
+ " Cloneable[] anArrayOfCloneables;\r\n" +
+ "\r\n" +
+ "}\r\n" +
+ "", true, null);
+
+ IField[] fields= getFields(a.getType("A"), new String[] { "aBool", "aByte", "aChar", "anInt", "aDouble", "aFloat", "aLong", "aString", "aListOfStrings", "anArrayOfCloneables" });
+ runJ7Operation(a.getType("A"), fields, false);
+
+ String expected= "package p;\r\n" +
+ "import java.util.Arrays;\r\n" +
+ "import java.util.List;\r\n" +
+ "import java.util.Objects;\r\n" +
+ "public class A {\r\n" +
+ " boolean aBool;\r\n" +
+ " byte aByte;\r\n" +
+ " char aChar;\r\n" +
+ " int anInt;\r\n" +
+ " double aDouble;\r\n" +
+ " float aFloat;\r\n" +
+ " long aLong;\r\n" +
+ " String aString;\r\n" +
+ " List<String> aListOfStrings;\r\n" +
+ " Cloneable[] anArrayOfCloneables;\r\n" +
+ " /* (non-Javadoc)\r\n" +
+ " * @see java.lang.Object#hashCode()\r\n" +
+ " */\r\n" +
+ " @Override\r\n" +
+ " public int hashCode() {\r\n" +
+ " final int prime = 31;\r\n" +
+ " int result = 1;\r\n" +
+ " result = prime * result + Objects.hash(aBool, aByte, aChar, anInt, aDouble, aFloat, aLong, aString, aListOfStrings);\r\n" +
+ " result = prime * result + Arrays.deepHashCode(anArrayOfCloneables);\r\n" +
+ " return result;\r\n" +
+ " }\r\n" +
+ " /* (non-Javadoc)\r\n" +
+ " * @see java.lang.Object#equals(java.lang.Object)\r\n" +
+ " */\r\n" +
+ " @Override\r\n" +
+ " public boolean equals(Object obj) {\r\n" +
+ " if (this == obj)\r\n" +
+ " return true;\r\n" +
+ " if (obj == null)\r\n" +
+ " return false;\r\n" +
+ " if (getClass() != obj.getClass())\r\n" +
+ " return false;\r\n" +
+ " A other = (A) obj;\r\n" +
+ " return aBool == other.aBool && aByte == other.aByte && aChar == other.aChar && anInt == other.anInt && Double.doubleToLongBits(aDouble) == Double.doubleToLongBits(other.aDouble) && Float.floatToIntBits(aFloat) == Float.floatToIntBits(other.aFloat) && aLong == other.aLong && Objects.equals(aString, other.aString) && Objects.equals(aListOfStrings, other.aListOfStrings) && Arrays.deepEquals(anArrayOfCloneables, other.anArrayOfCloneables);\r\n"
+ +
+ " }\r\n" +
+ "\r\n" +
+ "}\r\n" +
+ "";
+
+ compareSource(expected, a.getSource());
+ }
+
+ /**
+ * Test with J7+ Objects.hash and Objects.equals method calls
+ * Using 1-dim Serializable array amongst other instance variables
+ * @throws Exception
+ */
+ public void testHashCodeEqualsSerializableArrayIn17() throws Exception {
+
+ ICompilationUnit a= fPackageP.createCompilationUnit("A.java", "package p;\r\n" +
+ "import java.util.List;\r\n" +
+ "import java.io.Serializable;\r\n" +
+ "public class A {\r\n" +
+ " boolean aBool;\r\n" +
+ " byte aByte;\r\n" +
+ " char aChar;\r\n" +
+ " int anInt;\r\n" +
+ " double aDouble;\r\n" +
+ " float aFloat;\r\n" +
+ " long aLong;\r\n" +
+ " String aString;\r\n" +
+ " List<String> aListOfStrings;\r\n" +
+ " Serializable[] anArrayOfSerializables;\r\n" +
+ "\r\n" +
+ "}\r\n" +
+ "", true, null);
+
+ IField[] fields= getFields(a.getType("A"), new String[] { "aBool", "aByte", "aChar", "anInt", "aDouble", "aFloat", "aLong", "aString", "aListOfStrings", "anArrayOfSerializables" });
+ runJ7Operation(a.getType("A"), fields, false);
+
+ String expected= "package p;\r\n" +
+ "import java.util.Arrays;\r\n" +
+ "import java.util.List;\r\n" +
+ "import java.util.Objects;\r\n" +
+ "import java.io.Serializable;\r\n" +
+ "public class A {\r\n" +
+ " boolean aBool;\r\n" +
+ " byte aByte;\r\n" +
+ " char aChar;\r\n" +
+ " int anInt;\r\n" +
+ " double aDouble;\r\n" +
+ " float aFloat;\r\n" +
+ " long aLong;\r\n" +
+ " String aString;\r\n" +
+ " List<String> aListOfStrings;\r\n" +
+ " Serializable[] anArrayOfSerializables;\r\n" +
+ " /* (non-Javadoc)\r\n" +
+ " * @see java.lang.Object#hashCode()\r\n" +
+ " */\r\n" +
+ " @Override\r\n" +
+ " public int hashCode() {\r\n" +
+ " final int prime = 31;\r\n" +
+ " int result = 1;\r\n" +
+ " result = prime * result + Objects.hash(aBool, aByte, aChar, anInt, aDouble, aFloat, aLong, aString, aListOfStrings);\r\n" +
+ " result = prime * result + Arrays.deepHashCode(anArrayOfSerializables);\r\n" +
+ " return result;\r\n" +
+ " }\r\n" +
+ " /* (non-Javadoc)\r\n" +
+ " * @see java.lang.Object#equals(java.lang.Object)\r\n" +
+ " */\r\n" +
+ " @Override\r\n" +
+ " public boolean equals(Object obj) {\r\n" +
+ " if (this == obj)\r\n" +
+ " return true;\r\n" +
+ " if (obj == null)\r\n" +
+ " return false;\r\n" +
+ " if (getClass() != obj.getClass())\r\n" +
+ " return false;\r\n" +
+ " A other = (A) obj;\r\n" +
+ " return aBool == other.aBool && aByte == other.aByte && aChar == other.aChar && anInt == other.anInt && Double.doubleToLongBits(aDouble) == Double.doubleToLongBits(other.aDouble) && Float.floatToIntBits(aFloat) == Float.floatToIntBits(other.aFloat) && aLong == other.aLong && Objects.equals(aString, other.aString) && Objects.equals(aListOfStrings, other.aListOfStrings) && Arrays.deepEquals(anArrayOfSerializables, other.anArrayOfSerializables);\r\n"
+ +
+ " }\r\n" +
+ "\r\n" +
+ "}\r\n" +
+ "";
+
+ compareSource(expected, a.getSource());
+ }
+
+ /**
+ * Test with J7+ Objects.hash and Objects.equals method calls
+ * Using 1-dim Object array amongst other instance variables
+ * @throws Exception
+ */
+ public void testHashCodeEqualsObjectArrayIn17() throws Exception {
+
+ ICompilationUnit a= fPackageP.createCompilationUnit("A.java", "package p;\r\n" +
+ "import java.util.List;\r\n" +
+ "public class A {\r\n" +
+ " boolean aBool;\r\n" +
+ " byte aByte;\r\n" +
+ " char aChar;\r\n" +
+ " int anInt;\r\n" +
+ " double aDouble;\r\n" +
+ " float aFloat;\r\n" +
+ " long aLong;\r\n" +
+ " String aString;\r\n" +
+ " List<String> aListOfStrings;\r\n" +
+ " Object[] anArrayOfObjects;\r\n" +
+ "\r\n" +
+ "}\r\n" +
+ "", true, null);
+
+ IField[] fields= getFields(a.getType("A"), new String[] { "aBool", "aByte", "aChar", "anInt", "aDouble", "aFloat", "aLong", "aString", "aListOfStrings", "anArrayOfObjects" });
+ runJ7Operation(a.getType("A"), fields, false);
+
+ String expected= "package p;\r\n" +
+ "import java.util.Arrays;\r\n" +
+ "import java.util.List;\r\n" +
+ "import java.util.Objects;\r\n" +
+ "public class A {\r\n" +
+ " boolean aBool;\r\n" +
+ " byte aByte;\r\n" +
+ " char aChar;\r\n" +
+ " int anInt;\r\n" +
+ " double aDouble;\r\n" +
+ " float aFloat;\r\n" +
+ " long aLong;\r\n" +
+ " String aString;\r\n" +
+ " List<String> aListOfStrings;\r\n" +
+ " Object[] anArrayOfObjects;\r\n" +
+ " /* (non-Javadoc)\r\n" +
+ " * @see java.lang.Object#hashCode()\r\n" +
+ " */\r\n" +
+ " @Override\r\n" +
+ " public int hashCode() {\r\n" +
+ " final int prime = 31;\r\n" +
+ " int result = 1;\r\n" +
+ " result = prime * result + Objects.hash(aBool, aByte, aChar, anInt, aDouble, aFloat, aLong, aString, aListOfStrings);\r\n" +
+ " result = prime * result + Arrays.deepHashCode(anArrayOfObjects);\r\n" +
+ " return result;\r\n" +
+ " }\r\n" +
+ " /* (non-Javadoc)\r\n" +
+ " * @see java.lang.Object#equals(java.lang.Object)\r\n" +
+ " */\r\n" +
+ " @Override\r\n" +
+ " public boolean equals(Object obj) {\r\n" +
+ " if (this == obj)\r\n" +
+ " return true;\r\n" +
+ " if (obj == null)\r\n" +
+ " return false;\r\n" +
+ " if (getClass() != obj.getClass())\r\n" +
+ " return false;\r\n" +
+ " A other = (A) obj;\r\n" +
+ " return aBool == other.aBool && aByte == other.aByte && aChar == other.aChar && anInt == other.anInt && Double.doubleToLongBits(aDouble) == Double.doubleToLongBits(other.aDouble) && Float.floatToIntBits(aFloat) == Float.floatToIntBits(other.aFloat) && aLong == other.aLong && Objects.equals(aString, other.aString) && Objects.equals(aListOfStrings, other.aListOfStrings) && Arrays.deepEquals(anArrayOfObjects, other.anArrayOfObjects);\r\n"
+ +
+ " }\r\n" +
+ "\r\n" +
+ "}\r\n" +
+ "";
+
+ compareSource(expected, a.getSource());
+ }
+
+ /**
+ * Test with J7+ Objects.hash and Objects.equals method calls
+ * Using 1-dim type variable arrays extending Serializable and Number
+ * @throws Exception
+ */
+ public void testHashCodeEqualsTypeVariableArrayIn17() throws Exception {
+
+ ICompilationUnit a= fPackageP.createCompilationUnit("A.java", "package p;\r\n" +
+ "import java.io.Serializable;\r\n" +
+ "public class A <S extends Serializable, N extends Number> {\r\n" +
+ " S[] anArrayOfS;\r\n" +
+ " N[] anArrayOfN;\r\n" +
+ "\r\n" +
+ "}\r\n" +
+ "", true, null);
+
+ IField[] fields= getFields(a.getType("A"), new String[] { "anArrayOfS", "anArrayOfN" });
+ runJ7Operation(a.getType("A"), fields, false);
+
+ String expected= "package p;\r\n" +
+ "import java.io.Serializable;\r\n" +
+ "import java.util.Arrays;\r\n" +
+ "public class A <S extends Serializable, N extends Number> {\r\n" +
+ " S[] anArrayOfS;\r\n" +
+ " N[] anArrayOfN;\r\n" +
+ " /* (non-Javadoc)\r\n" +
+ " * @see java.lang.Object#hashCode()\r\n" +
+ " */\r\n" +
+ " @Override\r\n" +
+ " public int hashCode() {\r\n" +
+ " final int prime = 31;\r\n" +
+ " int result = 1;\r\n" +
+ " result = prime * result + Arrays.deepHashCode(anArrayOfS);\r\n" +
+ " result = prime * result + Arrays.hashCode(anArrayOfN);\r\n" +
+ " return result;\r\n" +
+ " }\r\n" +
+ " /* (non-Javadoc)\r\n" +
+ " * @see java.lang.Object#equals(java.lang.Object)\r\n" +
+ " */\r\n" +
+ " @Override\r\n" +
+ " public boolean equals(Object obj) {\r\n" +
+ " if (this == obj)\r\n" +
+ " return true;\r\n" +
+ " if (obj == null)\r\n" +
+ " return false;\r\n" +
+ " if (getClass() != obj.getClass())\r\n" +
+ " return false;\r\n" +
+ " A other = (A) obj;\r\n" +
+ " return Arrays.deepEquals(anArrayOfS, other.anArrayOfS) && Arrays.equals(anArrayOfN, other.anArrayOfN);\r\n"
+ +
+ " }\r\n" +
+ "\r\n" +
+ "}\r\n" +
+ "";
+
+ compareSource(expected, a.getSource());
+ }
+
+ /**
+ * Test with J7+ Objects.hash and Objects.equals method calls
+ * Using multidimensional array amongst other instance variables
+ * @throws Exception
+ */
+ public void testHashCodeEqualsMultiArrayIn17() throws Exception {
+
+ ICompilationUnit a= fPackageP.createCompilationUnit("A.java", "package p;\r\n" +
+ "import java.util.List;\r\n" +
+ "public class A {\r\n" +
+ " boolean aBool;\r\n" +
+ " byte aByte;\r\n" +
+ " char aChar;\r\n" +
+ " int anInt;\r\n" +
+ " double aDouble;\r\n" +
+ " float aFloat;\r\n" +
+ " long aLong;\r\n" +
+ " String aString;\r\n" +
+ " List<String> aListOfStrings;\r\n" +
+ " int[][] anArrayOfInts;\r\n" +
+ "\r\n" +
+ "}\r\n" +
+ "", true, null);
+
+ IField[] fields= getFields(a.getType("A"), new String[] { "aBool", "aByte", "aChar", "anInt", "aDouble", "aFloat", "aLong", "aString", "aListOfStrings", "anArrayOfInts" });
+ runJ7Operation(a.getType("A"), fields, false);
+
+ String expected= "package p;\r\n" +
+ "import java.util.Arrays;\r\n" +
+ "import java.util.List;\r\n" +
+ "import java.util.Objects;\r\n" +
+ "public class A {\r\n" +
+ " boolean aBool;\r\n" +
+ " byte aByte;\r\n" +
+ " char aChar;\r\n" +
+ " int anInt;\r\n" +
+ " double aDouble;\r\n" +
+ " float aFloat;\r\n" +
+ " long aLong;\r\n" +
+ " String aString;\r\n" +
+ " List<String> aListOfStrings;\r\n" +
+ " int[][] anArrayOfInts;\r\n" +
+ " /* (non-Javadoc)\r\n" +
+ " * @see java.lang.Object#hashCode()\r\n" +
+ " */\r\n" +
+ " @Override\r\n" +
+ " public int hashCode() {\r\n" +
+ " final int prime = 31;\r\n" +
+ " int result = 1;\r\n" +
+ " result = prime * result + Objects.hash(aBool, aByte, aChar, anInt, aDouble, aFloat, aLong, aString, aListOfStrings);\r\n" +
+ " result = prime * result + Arrays.deepHashCode(anArrayOfInts);\r\n" +
+ " return result;\r\n" +
+ " }\r\n" +
+ " /* (non-Javadoc)\r\n" +
+ " * @see java.lang.Object#equals(java.lang.Object)\r\n" +
+ " */\r\n" +
+ " @Override\r\n" +
+ " public boolean equals(Object obj) {\r\n" +
+ " if (this == obj)\r\n" +
+ " return true;\r\n" +
+ " if (obj == null)\r\n" +
+ " return false;\r\n" +
+ " if (getClass() != obj.getClass())\r\n" +
+ " return false;\r\n" +
+ " A other = (A) obj;\r\n" +
+ " return aBool == other.aBool && aByte == other.aByte && aChar == other.aChar && anInt == other.anInt && Double.doubleToLongBits(aDouble) == Double.doubleToLongBits(other.aDouble) && Float.floatToIntBits(aFloat) == Float.floatToIntBits(other.aFloat) && aLong == other.aLong && Objects.equals(aString, other.aString) && Objects.equals(aListOfStrings, other.aListOfStrings) && Arrays.deepEquals(anArrayOfInts, other.anArrayOfInts);\r\n"
+ +
+ " }\r\n" +
+ "\r\n" +
+ "}\r\n" +
+ "";
+
+ compareSource(expected, a.getSource());
+ }
+
+ /**
+ * Test with J7+ Objects.hash and Objects.equals method calls
+ * Using both multidimensional and 1-dimensional primitive arrays amongst other instance variables
+ *
+ * @throws Exception
+ */
+ public void testHashCodeEqualsVariousArraysIn17() throws Exception {
+
+ ICompilationUnit a= fPackageP.createCompilationUnit("A.java", "package p;\r\n" +
+ "import java.util.List;\r\n" +
+ "public class A {\r\n" +
+ " boolean aBool;\r\n" +
+ " byte aByte;\r\n" +
+ " char aChar;\r\n" +
+ " int anInt;\r\n" +
+ " double aDouble;\r\n" +
+ " float aFloat;\r\n" +
+ " long aLong;\r\n" +
+ " String aString;\r\n" +
+ " List<String> aListOfStrings;\r\n" +
+ " int[] anArrayOfInts;\r\n" +
+ " String[][] anArrayOfStrings;\r\n" +
+ "\r\n" +
+ "}\r\n" +
+ "", true, null);
+
+ IField[] fields= getFields(a.getType("A"), new String[] { "aBool", "aByte", "aChar", "anInt", "aDouble", "aFloat", "aLong", "aString", "aListOfStrings", "anArrayOfInts", "anArrayOfStrings" });
+ runJ7Operation(a.getType("A"), fields, false);
+
+ String expected= "package p;\r\n" +
+ "import java.util.Arrays;\r\n" +
+ "import java.util.List;\r\n" +
+ "import java.util.Objects;\r\n" +
+ "public class A {\r\n" +
+ " boolean aBool;\r\n" +
+ " byte aByte;\r\n" +
+ " char aChar;\r\n" +
+ " int anInt;\r\n" +
+ " double aDouble;\r\n" +
+ " float aFloat;\r\n" +
+ " long aLong;\r\n" +
+ " String aString;\r\n" +
+ " List<String> aListOfStrings;\r\n" +
+ " int[] anArrayOfInts;\r\n" +
+ " String[][] anArrayOfStrings;\r\n" +
+ " /* (non-Javadoc)\r\n" +
+ " * @see java.lang.Object#hashCode()\r\n" +
+ " */\r\n" +
+ " @Override\r\n" +
+ " public int hashCode() {\r\n" +
+ " final int prime = 31;\r\n" +
+ " int result = 1;\r\n" +
+ " result = prime * result + Objects.hash(aBool, aByte, aChar, anInt, aDouble, aFloat, aLong, aString, aListOfStrings);\r\n" +
+ " result = prime * result + Arrays.hashCode(anArrayOfInts);\r\n" +
+ " result = prime * result + Arrays.deepHashCode(anArrayOfStrings);\r\n" +
+ " return result;\r\n" +
+ " }\r\n" +
+ " /* (non-Javadoc)\r\n" +
+ " * @see java.lang.Object#equals(java.lang.Object)\r\n" +
+ " */\r\n" +
+ " @Override\r\n" +
+ " public boolean equals(Object obj) {\r\n" +
+ " if (this == obj)\r\n" +
+ " return true;\r\n" +
+ " if (obj == null)\r\n" +
+ " return false;\r\n" +
+ " if (getClass() != obj.getClass())\r\n" +
+ " return false;\r\n" +
+ " A other = (A) obj;\r\n" +
+ " return aBool == other.aBool && aByte == other.aByte && aChar == other.aChar && anInt == other.anInt && Double.doubleToLongBits(aDouble) == Double.doubleToLongBits(other.aDouble) && Float.floatToIntBits(aFloat) == Float.floatToIntBits(other.aFloat) && aLong == other.aLong && Objects.equals(aString, other.aString) && Objects.equals(aListOfStrings, other.aListOfStrings) && Arrays.equals(anArrayOfInts, other.anArrayOfInts) && Arrays.deepEquals(anArrayOfStrings, other.anArrayOfStrings);\r\n"
+ +
+ " }\r\n" +
+ "\r\n" +
+ "}\r\n" +
+ "";
+
+ compareSource(expected, a.getSource());
+ }
+
+ /**
+ * Test with J7+ Objects.hash and Objects.equals method calls
+ * Using ONLY multidimensional and 1-dimensional arrays as instance variables
+ *
+ * @throws Exception
+ */
+ public void testHashCodeEqualsOnlyArraysIn17() throws Exception {
+
+ ICompilationUnit a= fPackageP.createCompilationUnit("A.java", "package p;\r\n" +
+ "public class A {\r\n" +
+ " int[] anArrayOfInts;\r\n" +
+ " String[][] anArrayOfStrings;\r\n" +
+ "\r\n" +
+ "}\r\n" +
+ "", true, null);
+
+ IField[] fields= getFields(a.getType("A"), new String[] { "anArrayOfInts", "anArrayOfStrings" });
+ runJ7Operation(a.getType("A"), fields, false);
+
+ String expected= "package p;\r\n" +
+ "\r\n" +
+ "import java.util.Arrays;\r\n" +
+ "\r\n" +
+ "public class A {\r\n" +
+ " int[] anArrayOfInts;\r\n" +
+ " String[][] anArrayOfStrings;\r\n" +
+ " /* (non-Javadoc)\r\n" +
+ " * @see java.lang.Object#hashCode()\r\n" +
+ " */\r\n" +
+ " @Override\r\n" +
+ " public int hashCode() {\r\n" +
+ " final int prime = 31;\r\n" +
+ " int result = 1;\r\n" +
+ " result = prime * result + Arrays.hashCode(anArrayOfInts);\r\n" +
+ " result = prime * result + Arrays.deepHashCode(anArrayOfStrings);\r\n" +
+ " return result;\r\n" +
+ " }\r\n" +
+ " /* (non-Javadoc)\r\n" +
+ " * @see java.lang.Object#equals(java.lang.Object)\r\n" +
+ " */\r\n" +
+ " @Override\r\n" +
+ " public boolean equals(Object obj) {\r\n" +
+ " if (this == obj)\r\n" +
+ " return true;\r\n" +
+ " if (obj == null)\r\n" +
+ " return false;\r\n" +
+ " if (getClass() != obj.getClass())\r\n" +
+ " return false;\r\n" +
+ " A other = (A) obj;\r\n" +
+ " return Arrays.equals(anArrayOfInts, other.anArrayOfInts) && Arrays.deepEquals(anArrayOfStrings, other.anArrayOfStrings);\r\n" +
+ " }\r\n" +
+ "\r\n" +
+ "}\r\n" +
+ "";
+
+ compareSource(expected, a.getSource());
+ }
+
+ /**
* Test member types
*
* @throws Exception
@@ -1139,7 +1913,7 @@
"}", true, null);
IField[] fields= getFields(a.getType("A"), new String[] {"aBool", "obj" });
- runOperation(a.getType("A"), fields, null, true, false, true, false);
+ runOperation(a.getType("A"), fields, null, true, false, false, true, false);
String expected= "package p;\r\n" +
"\r\n" +
@@ -1346,7 +2120,7 @@
IJavaElement insertBefore= i < NUM_MEMBERS ? children[i] : null;
- runOperation(type, new IField[] { foo }, insertBefore, false, false, false, false);
+ runOperation(type, new IField[] { foo }, insertBefore, false, false, false, false, false);
IJavaElement[] newChildren= type.getChildren();
assertEquals(NUM_MEMBERS + 2, newChildren.length);
@@ -1381,7 +2155,7 @@
"", true, null);
IField[] fields= getFields(a.getType("Sub"), new String[] {"name" });
- runOperation(a.getType("Sub"), fields, null, false, false, false, false);
+ runOperation(a.getType("Sub"), fields, null, false, false, false, false, false);
String expected= "package p;\r\n" +
"\r\n" +
diff --git a/org.eclipse.jdt.ui/core extension/org/eclipse/jdt/internal/corext/codemanipulation/GenerateHashCodeEqualsOperation.java b/org.eclipse.jdt.ui/core extension/org/eclipse/jdt/internal/corext/codemanipulation/GenerateHashCodeEqualsOperation.java
index b95772a..eb87f01 100644
--- a/org.eclipse.jdt.ui/core extension/org/eclipse/jdt/internal/corext/codemanipulation/GenerateHashCodeEqualsOperation.java
+++ b/org.eclipse.jdt.ui/core extension/org/eclipse/jdt/internal/corext/codemanipulation/GenerateHashCodeEqualsOperation.java
@@ -7,6 +7,7 @@
*
* Contributors:
* IBM Corporation - initial API and implementation
+ * Pierre-Yves B. <pyvesdev@gmail.com> - Generation of equals and hashcode with java 7 Objects.equals and Objects.hashcode - https://bugs.eclipse.org/424214
*******************************************************************************/
package org.eclipse.jdt.internal.corext.codemanipulation;
@@ -144,6 +145,8 @@
private static final String METHODNAME_HASH_CODE= "hashCode"; //$NON-NLS-1$
+ private static final String METHODNAME_HASH= "hash"; //$NON-NLS-1$
+
private static final String METHODNAME_DEEP_HASH_CODE= "deepHashCode"; //$NON-NLS-1$
private static final String METHODNAME_OUTER_TYPE= "getOuterType"; //$NON-NLS-1$
@@ -166,6 +169,14 @@
private static final String VARIABLE_NAME_INDEX= "index"; //$NON-NLS-1$
+ private static final String JAVA_UTIL_OBJECTS= "java.util.Objects"; //$NON-NLS-1$
+
+ private static final String TYPE_NAME_CLONEABLE= "Cloneable"; //$NON-NLS-1$
+
+ private static final String TYPE_NAME_SERIALIZABLE= "Serializable"; //$NON-NLS-1$
+
+ private static final String TYPE_NAME_OBJECT= "Object"; //$NON-NLS-1$
+
/** Should the resulting edit be applied? */
private final boolean fApply;
@@ -208,6 +219,12 @@
/** <code>true</code> to use 'instanceof' to compare types, <code>false</code> otherwise */
private final boolean fUseInstanceOf;
+ /**
+ * <code>true</code> to use newer hashcode and equals method generation, using Java 7+
+ * Objects.hash and Objects.equals, <code>false</code> to generate default methods
+ */
+ private final boolean fUseJ7HashEquals;
+
/** <code>true</code> to use blocks for then */
private boolean fUseBlocksForThen;
@@ -223,6 +240,7 @@
* @param insert the insertion point, or <code>null</code>
* @param settings the code generation settings to use
* @param useInstanceof <code>true</code> to use 'instanceof' to compare types, <code>false</code> otherwise
+ * @param useJ7HashEquals <code>true</code> to use Java 7+ Objects.hash and Objects.equals methods, <code>false</code> otherwise
* @param force <code>true</code> to force the regeneration of existing methods,
* <code>false</code> otherwise
* @param apply <code>true</code> if the resulting edit should be applied,
@@ -231,7 +249,8 @@
* saved, <code>false</code> otherwise
*/
public GenerateHashCodeEqualsOperation(final ITypeBinding type, final IVariableBinding[] fields, final CompilationUnit unit,
- final IJavaElement insert, final CodeGenerationSettings settings, final boolean useInstanceof, final boolean force, final boolean apply, final boolean save) {
+ final IJavaElement insert, final CodeGenerationSettings settings, final boolean useInstanceof, final boolean useJ7HashEquals, final boolean force, final boolean apply,
+ final boolean save) {
Assert.isNotNull(type);
Assert.isNotNull(fields);
Assert.isNotNull(unit);
@@ -244,6 +263,7 @@
fFields= fields;
fSettings= settings;
fUseInstanceOf= useInstanceof;
+ fUseJ7HashEquals= useJ7HashEquals;
fSave= save;
fApply= apply;
fDoubleCount= 0;
@@ -396,7 +416,7 @@
Block body= fAst.newBlock();
hashCodeMethod.setBody(body);
-
+
// PRIME NUMBER
VariableDeclarationFragment frag= fAst.newVariableDeclarationFragment();
frag.setName(fAst.newSimpleName(VARIABLE_NAME_PRIME));
@@ -405,15 +425,16 @@
VariableDeclarationStatement primeNumberDeclaration= fAst.newVariableDeclarationStatement(frag);
primeNumberDeclaration.modifiers().add(fAst.newModifier(ModifierKeyword.FINAL_KEYWORD));
primeNumberDeclaration.setType(fAst.newPrimitiveType(PrimitiveType.INT));
- body.statements().add(primeNumberDeclaration);
+ if (!fUseJ7HashEquals)
+ body.statements().add(primeNumberDeclaration);
- // RESULT
VariableDeclarationFragment fragment= fAst.newVariableDeclarationFragment();
fragment.setName(fAst.newSimpleName(VARIABLE_NAME_RESULT));
VariableDeclarationStatement resultDeclaration= fAst.newVariableDeclarationStatement(fragment);
resultDeclaration.setType(fAst.newPrimitiveType(PrimitiveType.INT));
- body.statements().add(resultDeclaration);
+ if (!fUseJ7HashEquals)
+ body.statements().add(resultDeclaration);
if (needsNoSuperCall(fType, METHODNAME_HASH_CODE, new ITypeBinding[0])) {
fragment.setInitializer(fAst.newNumberLiteral(INITIAL_HASHCODE_VALUE));
@@ -427,28 +448,56 @@
body.statements().add(createAddOuterHashCode());
}
- for (int i= 0; i < fFields.length; i++) {
- if (fFields[i].getType().isPrimitive()) {
- Statement[] sts= createAddSimpleHashCode(fFields[i].getType(), new IHashCodeAccessProvider() {
+ ReturnStatement endReturn= fAst.newReturnStatement();
+ if (fUseJ7HashEquals) {
+ MethodInvocation j7Invoc= fAst.newMethodInvocation();
+ List<Statement> arrayStatements= new ArrayList<>();
- @Override
- public Expression getThisAccess(String name) {
- return getThisAccessForHashCode(name);
+ for (int i= 0; i < fFields.length; i++) {
+ if (fFields[i].getType().isArray())
+ arrayStatements.add(createAddArrayHashCode(fFields[i]));
+ else
+ j7Invoc.arguments().add(fAst.newSimpleName(fFields[i].getName()));
+ }
+
+ if (!j7Invoc.arguments().isEmpty()) {
+ j7Invoc.setExpression(getQualifiedName(JAVA_UTIL_OBJECTS));
+ j7Invoc.setName(fAst.newSimpleName(METHODNAME_HASH));
+ }
+
+ if (arrayStatements.isEmpty()) {
+ endReturn.setExpression(j7Invoc);
+ } else {
+ body.statements().add(primeNumberDeclaration);
+ body.statements().add(resultDeclaration);
+ if (!j7Invoc.arguments().isEmpty())
+ body.statements().add(prepareAssignment(j7Invoc));
+ body.statements().addAll(arrayStatements);
+ endReturn.setExpression(fAst.newSimpleName(VARIABLE_NAME_RESULT));
+ }
+ } else {
+ for (int i= 0; i < fFields.length; i++) {
+ if (fFields[i].getType().isPrimitive()) {
+ Statement[] sts= createAddSimpleHashCode(fFields[i].getType(), new IHashCodeAccessProvider() {
+
+ @Override
+ public Expression getThisAccess(String name) {
+ return getThisAccessForHashCode(name);
+ }
+
+ }, fFields[i].getName(), false);
+ for (int j= 0; j < sts.length; j++) {
+ body.statements().add(sts[j]);
}
-
- }, fFields[i].getName(), false);
- for (int j= 0; j < sts.length; j++) {
- body.statements().add(sts[j]);
- }
- } else if (fFields[i].getType().isArray())
- body.statements().add(createAddArrayHashCode(fFields[i]));
- else
- body.statements().add(createAddQualifiedHashCode(fFields[i]));
+ } else if (fFields[i].getType().isArray())
+ body.statements().add(createAddArrayHashCode(fFields[i]));
+ else
+ body.statements().add(createAddQualifiedHashCode(fFields[i]));
+ }
+ endReturn.setExpression(fAst.newSimpleName(VARIABLE_NAME_RESULT));
}
// the last return:
- ReturnStatement endReturn= fAst.newReturnStatement();
- endReturn.setExpression(fAst.newSimpleName(VARIABLE_NAME_RESULT));
body.statements().add(endReturn);
// method comment
@@ -546,7 +595,7 @@
private Statement createAddArrayHashCode(IVariableBinding binding) {
MethodInvocation invoc= fAst.newMethodInvocation();
if (JavaModelUtil.is50OrHigher(fRewrite.getCu().getJavaProject())) {
- if (binding.getType().getDimensions() > 1) {
+ if (needsDeepMethod(binding.getType())) {
invoc.setName(fAst.newSimpleName(METHODNAME_DEEP_HASH_CODE));
} else {
invoc.setName(fAst.newSimpleName(METHODNAME_HASH_CODE));
@@ -860,33 +909,37 @@
body.statements().add(otherDeclaration);
- if (isMemberType()) { // test outer type
- body.statements().add(createOuterComparison());
+ if (fUseJ7HashEquals) {
+ body.statements().add(createJ7EqualsStatement());
+ } else {
+ if (isMemberType()) { // test outer type
+ body.statements().add(createOuterComparison());
+ }
+
+ for (int i= 0; i < fFields.length; i++) {
+ IVariableBinding field= fFields[i];
+ ITypeBinding type= field.getType();
+ if (type.isPrimitive() || type.isEnum())
+ body.statements().add(createSimpleComparison(field));
+ else if (type.isArray()) {
+ IJavaProject project= fUnit.getJavaElement().getJavaProject();
+ if (needsDeepMethod(type) && JavaModelUtil.is50OrHigher(project)) {
+ body.statements().add(createMultiArrayComparison(field.getName()));
+ } else {
+ body.statements().add(createArrayComparison(field.getName()));
+ }
+ } else
+ body.statements().add(createQualifiedComparison(field.getName()));
+
+ }
+
+ // the last return true:
+ ReturnStatement endReturn= fAst.newReturnStatement();
+ endReturn.setExpression(fAst.newBooleanLiteral(true));
+
+ body.statements().add(endReturn);
}
- for (int i= 0; i < fFields.length; i++) {
- IVariableBinding field= fFields[i];
- ITypeBinding type= field.getType();
- if (type.isPrimitive() || type.isEnum())
- body.statements().add(createSimpleComparison(field));
- else if (type.isArray()) {
- IJavaProject project= fUnit.getJavaElement().getJavaProject();
- if (type.getDimensions() > 1 && JavaModelUtil.is50OrHigher(project)) {
- body.statements().add(createMultiArrayComparison(field.getName()));
- } else {
- body.statements().add(createArrayComparison(field.getName()));
- }
- } else
- body.statements().add(createQualifiedComparison(field.getName()));
-
- }
-
- // the last return true:
- ReturnStatement endReturn= fAst.newReturnStatement();
- endReturn.setExpression(fAst.newBooleanLiteral(true));
-
- body.statements().add(endReturn);
-
// method comment
if (fSettings != null) {
ITypeBinding object= fAst.resolveWellKnownType(JAVA_LANG_OBJECT);
@@ -903,6 +956,72 @@
return equalsMethodDeclaration;
}
+ private Statement createJ7EqualsStatement() {
+ // return Object.equals(anObjet, other.anObject) && ... ;
+ ReturnStatement returnStatement= fAst.newReturnStatement();
+
+ Expression equalsExp= createJ7EqualsExpression(fFields[0]);
+ if (fFields.length == 1) {
+ returnStatement.setExpression(equalsExp);
+ } else {
+ InfixExpression exp= fAst.newInfixExpression();
+ exp.setLeftOperand(equalsExp);
+ InfixExpression refExp= exp;
+ for (int i= 1; i < fFields.length; i++) {
+ equalsExp= createJ7EqualsExpression(fFields[i]);
+
+ refExp.setOperator(Operator.CONDITIONAL_AND);
+ if (i == fFields.length - 1) {
+ refExp.setRightOperand(equalsExp);
+ } else {
+ InfixExpression infixExp= fAst.newInfixExpression();
+ infixExp.setLeftOperand(equalsExp);
+ refExp.setRightOperand(infixExp);
+ refExp= infixExp;
+ }
+ }
+ returnStatement.setExpression(exp);
+ }
+ return returnStatement;
+ }
+
+ private Expression createJ7EqualsExpression(IVariableBinding variable) {
+ ITypeBinding type= variable.getType();
+ String name= variable.getName();
+ if (type.isPrimitive() || type.isEnum()) {
+ InfixExpression expression= fAst.newInfixExpression();
+ expression.setOperator(Operator.EQUALS);
+ if (isPrimitiveType(type, PrimitiveType.FLOAT)) {
+ // Float.floatToIntBits(aFloat) == Float.floatToIntBits(other.aFloat)
+ expression.setLeftOperand(createFloatInvocation(getThisAccessForEquals(name)));
+ expression.setRightOperand(createFloatInvocation(getOtherAccess(name)));
+ } else if (isPrimitiveType(type, PrimitiveType.DOUBLE)) {
+ // Double.doubleToLongBits(aDouble) == Double.doubleToLongBits(other.aDouble)
+ expression.setLeftOperand(createDoubleInvocation(getThisAccessForEquals(name)));
+ expression.setRightOperand(createDoubleInvocation(getOtherAccess(name)));
+ } else {
+ // anInt == other.anInt
+ expression.setLeftOperand(getThisAccessForEquals(name));
+ expression.setRightOperand(getOtherAccess(name));
+ }
+ return expression;
+ } else {
+ MethodInvocation invoc= fAst.newMethodInvocation();
+ if (type.isArray()) {
+ // Arrays.equals(anArray, other.anArray) or Arrays.deepEquals(anArray, other.anArray)
+ invoc.setExpression(getQualifiedName(JAVA_UTIL_ARRAYS));
+ invoc.setName(needsDeepMethod(type) ? fAst.newSimpleName(METHODNAME_DEEP_EQUALS) : fAst.newSimpleName(METHODNAME_EQUALS));
+ } else {
+ // Objects.equals(anObj, other.anObj)
+ invoc.setExpression(getQualifiedName(JAVA_UTIL_OBJECTS));
+ invoc.setName(fAst.newSimpleName(METHODNAME_EQUALS));
+ }
+ invoc.arguments().add(fAst.newSimpleName(name));
+ invoc.arguments().add(getOtherAccess(name));
+ return invoc;
+ }
+ }
+
private Statement createOuterComparison() {
MethodInvocation outer1= fAst.newMethodInvocation();
outer1.setName(fAst.newSimpleName(METHODNAME_OUTER_TYPE));
@@ -1158,4 +1277,9 @@
return ( (name.equals(VARIABLE_NAME_EQUALS_CASTED)) || (name.equals(VARIABLE_NAME_EQUALS_PARAM)));
}
+ private boolean needsDeepMethod(ITypeBinding type) {
+ String elementTypeName= type.getErasure().getElementType().getName();
+ return type.getDimensions() > 1 || TYPE_NAME_CLONEABLE.equals(elementTypeName) || TYPE_NAME_SERIALIZABLE.equals(elementTypeName) || TYPE_NAME_OBJECT.equals(elementTypeName);
+ }
+
}
diff --git a/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/JavaUIMessages.java b/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/JavaUIMessages.java
index 09acc3f..be4de14 100644
--- a/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/JavaUIMessages.java
+++ b/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/JavaUIMessages.java
@@ -26,6 +26,7 @@
public static String FilteredTypesSelectionDialog_TypeFiltersPreferencesAction_label;
public static String GenerateHashCodeEqualsDialog_blocks_button;
+ public static String GenerateHashCodeEqualsDialog_j7hashequals_button;
public static String GenerateHashCodeEqualsDialog_instanceof_button;
public static String JavaPlugin_additionalInfo_affordance;
public static String JavaPlugin_internal_error;
diff --git a/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/JavaUIMessages.properties b/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/JavaUIMessages.properties
index 9220ae1..2b425df 100644
--- a/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/JavaUIMessages.properties
+++ b/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/JavaUIMessages.properties
@@ -1,5 +1,5 @@
###############################################################################
-# Copyright (c) 2000, 2017 IBM Corporation and others.
+# Copyright (c) 2000, 2018 IBM Corporation and others.
# All rights reserved. This program and the accompanying materials
# are made available under the terms of the Eclipse Public License v1.0
# which accompanies this distribution, and is available at
@@ -11,6 +11,7 @@
# Mateusz Matela <mateusz.matela@gmail.com> - [toString] Template edit dialog has usability issues - https://bugs.eclipse.org/bugs/show_bug.cgi?id=267916
# Mateusz Matela <mateusz.matela@gmail.com> - [toString] finish toString() builder wizard - https://bugs.eclipse.org/bugs/show_bug.cgi?id=267710
# Mateusz Matela <mateusz.matela@gmail.com> - [toString] toString() generator: Fields in declaration order - https://bugs.eclipse.org/bugs/show_bug.cgi?id=279924
+# Pierre-Yves B. <pyvesdev@gmail.com> - Generation of equals and hashcode with java 7 Objects.equals and Objects.hashcode - https://bugs.eclipse.org/bugs/show_bug.cgi?id=424214
###############################################################################
JavaPlugin_additionalInfo_affordance=Press 'Tab' from proposal table or click for focus
@@ -71,6 +72,7 @@
ProblemMarkerManager_problem_marker_update_job_description=Sending problem marker updates...
GenerateHashCodeEqualsDialog_blocks_button=Use &blocks in 'if' statements
+GenerateHashCodeEqualsDialog_j7hashequals_button=Use Objects.hash and Objects.equals methods (1.7 or higher)
GenerateHashCodeEqualsDialog_dialog_title=Generate hashCode() and equals()
GenerateHashCodeEqualsDialog_selectioninfo_more={0} of {1} selected.
GenerateHashCodeEqualsDialog_instanceof_button=&Use 'instanceof' to compare types
diff --git a/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/dialogs/GenerateHashCodeEqualsDialog.java b/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/dialogs/GenerateHashCodeEqualsDialog.java
index 38c9a17..36e22c1 100644
--- a/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/dialogs/GenerateHashCodeEqualsDialog.java
+++ b/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/dialogs/GenerateHashCodeEqualsDialog.java
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2005, 2017 IBM Corporation and others.
+ * Copyright (c) 2005, 2018 IBM Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
@@ -7,6 +7,7 @@
*
* Contributors:
* IBM Corporation - initial API and implementation
+ * Pierre-Yves B. <pyvesdev@gmail.com> - Generation of equals and hashcode with java 7 Objects.equals and Objects.hashcode - https://bugs.eclipse.org/424214
*******************************************************************************/
package org.eclipse.jdt.internal.ui.dialogs;
@@ -27,10 +28,12 @@
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.dialogs.ISelectionStatusValidator;
+import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.IType;
import org.eclipse.jdt.core.JavaModelException;
import org.eclipse.jdt.core.dom.IVariableBinding;
+import org.eclipse.jdt.internal.corext.util.JavaModelUtil;
import org.eclipse.jdt.internal.corext.util.Messages;
import org.eclipse.jdt.internal.ui.IJavaHelpContextIds;
@@ -119,12 +122,16 @@
private static final String SETTINGS_INSTANCEOF= "InstanceOf"; //$NON-NLS-1$
private static final String SETTINGS_BLOCKS= "Blocks"; //$NON-NLS-1$
+ private static final String SETTINGS_J7_HASH_EQUALS= "Objects.equals & Objects.hash"; //$NON-NLS-1$
private boolean fUseInstanceOf;
private boolean fUseBlocks;
+ private boolean fUseJ7HashEquals;
+ private IJavaProject fProject;
public GenerateHashCodeEqualsDialog(Shell shell, CompilationUnitEditor editor, IType type, IVariableBinding[] allFields, IVariableBinding[] selectedFields) throws JavaModelException {
super(shell, new BindingLabelProvider(), new GenerateHashCodeEqualsContentProvider(allFields), editor, type, false);
+ this.fProject = type.getJavaProject();
setEmptyListMessage(JavaUIMessages.GenerateHashCodeEqualsDialog_no_entries);
setInitialSelections((Object[]) selectedFields);
@@ -137,12 +144,14 @@
fUseInstanceOf= asBoolean(getDialogSettings().get(SETTINGS_INSTANCEOF), false);
fUseBlocks= asBoolean(getDialogSettings().get(SETTINGS_BLOCKS), false);
+ fUseJ7HashEquals= asBoolean(getDialogSettings().get(SETTINGS_J7_HASH_EQUALS), false);
}
@Override
public boolean close() {
getDialogSettings().put(SETTINGS_INSTANCEOF, fUseInstanceOf);
getDialogSettings().put(SETTINGS_BLOCKS, fUseBlocks);
+ getDialogSettings().put(SETTINGS_J7_HASH_EQUALS, fUseJ7HashEquals);
return super.close();
}
@@ -182,6 +191,23 @@
}
});
button.setSelection(isUseBlocks());
+
+ button= new Button(composite, SWT.CHECK);
+ button.setText(JavaUIMessages.GenerateHashCodeEqualsDialog_j7hashequals_button);
+ if (JavaModelUtil.is17OrHigher(this.fProject)) {
+ button.addSelectionListener(new SelectionAdapter() {
+
+ @Override
+ public void widgetSelected(SelectionEvent event) {
+ setUseJ7HashEquals((((Button) event.widget).getSelection()));
+ }
+ });
+ button.setSelection(isUseJ7HashEquals());
+ } else {
+ button.setEnabled(false);
+ button.setSelection(false);
+ setUseJ7HashEquals(false);
+ }
data= new GridData(GridData.HORIZONTAL_ALIGN_FILL);
data.horizontalSpan= 2;
button.setLayoutData(data);
@@ -205,6 +231,14 @@
fUseBlocks= useBlocks;
}
+ public boolean isUseJ7HashEquals() {
+ return fUseJ7HashEquals;
+ }
+
+ public void setUseJ7HashEquals(boolean useJ7HashEquals) {
+ fUseJ7HashEquals= useJ7HashEquals;
+ }
+
@Override
protected void createButtonsForButtonBar(Composite parent) {
createButton(parent, IDialogConstants.OK_ID, JavaUIMessages.GenerateHashCodeEqualsDialog_generate, true);
diff --git a/org.eclipse.jdt.ui/ui/org/eclipse/jdt/ui/actions/GenerateHashCodeEqualsAction.java b/org.eclipse.jdt.ui/ui/org/eclipse/jdt/ui/actions/GenerateHashCodeEqualsAction.java
index 340058a..da64766 100644
--- a/org.eclipse.jdt.ui/ui/org/eclipse/jdt/ui/actions/GenerateHashCodeEqualsAction.java
+++ b/org.eclipse.jdt.ui/ui/org/eclipse/jdt/ui/actions/GenerateHashCodeEqualsAction.java
@@ -85,6 +85,7 @@
private class HashCodeEqualsGenerationSettings extends CodeGenerationSettings {
public boolean useInstanceOf= false;
public boolean useBlocks= false;
+ public boolean useJ7HashEquals= false;
}
private List<IVariableBinding> allFields;
@@ -209,6 +210,7 @@
GenerateHashCodeEqualsDialog generateHashCodeEqualsDialog= (GenerateHashCodeEqualsDialog)dialog;
settings.useInstanceOf= generateHashCodeEqualsDialog.isUseInstanceOf();
settings.useBlocks= generateHashCodeEqualsDialog.isUseBlocks();
+ settings.useJ7HashEquals= generateHashCodeEqualsDialog.isUseJ7HashEquals();
return settings;
}
@@ -281,7 +283,7 @@
final IVariableBinding[] selectedVariableBindings= Arrays.asList(selectedBindings).toArray(new IVariableBinding[0]);
HashCodeEqualsGenerationSettings hashCodeEqualsGenerationSettings= (HashCodeEqualsGenerationSettings)settings;
GenerateHashCodeEqualsOperation operation= new GenerateHashCodeEqualsOperation(fTypeBinding, selectedVariableBindings, fUnit, elementPosition, settings,
- hashCodeEqualsGenerationSettings.useInstanceOf, regenerate, true, false);
+ hashCodeEqualsGenerationSettings.useInstanceOf, hashCodeEqualsGenerationSettings.useJ7HashEquals, regenerate, true, false);
operation.setUseBlocksForThen(hashCodeEqualsGenerationSettings.useBlocks);
return operation;
}