From 0a98c3fa94cf86add28dc6b845ec857544cdc296 Mon Sep 17 00:00:00 2001 From: Niels Berghen Date: Wed, 17 Apr 2024 15:13:23 +0200 Subject: [PATCH] Equals method not found on abstract class #709 --- .../json/internal/JsonTypeDescriptor.java | 8 ++- .../json/internal/JsonTypeDescriptorTest.java | 69 +++++++++++++++++++ .../json/internal/JsonTypeDescriptor.java | 8 ++- .../json/internal/JsonTypeDescriptorTest.java | 69 +++++++++++++++++++ .../json/internal/JsonJavaTypeDescriptor.java | 8 ++- .../json/internal/JsonTypeDescriptorTest.java | 69 +++++++++++++++++++ .../json/internal/JsonJavaTypeDescriptor.java | 8 ++- .../json/internal/JsonTypeDescriptorTest.java | 69 +++++++++++++++++++ .../json/internal/JsonJavaTypeDescriptor.java | 8 ++- .../json/internal/JsonTypeDescriptorTest.java | 69 +++++++++++++++++++ 10 files changed, 370 insertions(+), 15 deletions(-) diff --git a/hypersistence-utils-hibernate-52/src/main/java/io/hypersistence/utils/hibernate/type/json/internal/JsonTypeDescriptor.java b/hypersistence-utils-hibernate-52/src/main/java/io/hypersistence/utils/hibernate/type/json/internal/JsonTypeDescriptor.java index f12a40d80..22a727252 100644 --- a/hypersistence-utils-hibernate-52/src/main/java/io/hypersistence/utils/hibernate/type/json/internal/JsonTypeDescriptor.java +++ b/hypersistence-utils-hibernate-52/src/main/java/io/hypersistence/utils/hibernate/type/json/internal/JsonTypeDescriptor.java @@ -97,9 +97,11 @@ public boolean areEqual(Object one, Object another) { (one instanceof Map && another instanceof Map)) { return Objects.equals(one, another); } - if (one.getClass().equals(another.getClass()) && - ReflectionUtils.getDeclaredMethodOrNull(one.getClass(), "equals", Object.class) != null) { - return one.equals(another); + if (one.getClass().equals(another.getClass())) { + Method equalsMethod = ReflectionUtils.getMethodOrNull(one.getClass(), "equals", Object.class); + if (equalsMethod != null && !Object.class.equals(equalsMethod.getDeclaringClass())) { + return one.equals(another); + } } return objectMapperWrapper.toJsonNode(objectMapperWrapper.toString(one)).equals( objectMapperWrapper.toJsonNode(objectMapperWrapper.toString(another)) diff --git a/hypersistence-utils-hibernate-52/src/test/java/io/hypersistence/utils/hibernate/type/json/internal/JsonTypeDescriptorTest.java b/hypersistence-utils-hibernate-52/src/test/java/io/hypersistence/utils/hibernate/type/json/internal/JsonTypeDescriptorTest.java index 6115f3c2c..556da3917 100644 --- a/hypersistence-utils-hibernate-52/src/test/java/io/hypersistence/utils/hibernate/type/json/internal/JsonTypeDescriptorTest.java +++ b/hypersistence-utils-hibernate-52/src/test/java/io/hypersistence/utils/hibernate/type/json/internal/JsonTypeDescriptorTest.java @@ -42,6 +42,39 @@ public void testSetsAreEqual() { assertTrue(descriptor.areEqual(theFirst, theSecond)); } + /** + * When the JSON object contains an explicit equals method, even when defined on the abstract super class {@link AbstractForm}, + * that equals method should be used. + * If JSON serialization is used, + * the {@link JsonTypeDescriptor#areEqual(Object, Object)} depends on the values of the fields. + * + * The equals method of the {@link FormImpl} always returns true, so the two objects should be equal, + * even when having a different value. + */ + @Test + public void testAbstractClassImplementationsAreEqual() { + JsonTypeDescriptor descriptor = new JsonTypeDescriptor(); + + FormImpl firstEntity = new FormImpl("value1"); + FormImpl secondEntity = new FormImpl("value2"); + + assertTrue(descriptor.areEqual(firstEntity, secondEntity)); + } + + /** + * When the JSON object does not contain an explicit equals method, it should not use the equals method {@link Object}, + * but should use the ObjectWrapperMapper to equal the objects as JsonNodes + */ + @Test + public void testClassesWithoutEqualsMethodShouldEqualAsJsonNodes() { + JsonTypeDescriptor descriptor = new JsonTypeDescriptor(); + + FormWithoutEqualsMethod firstEntity = new FormWithoutEqualsMethod("value1"); + FormWithoutEqualsMethod secondEntity = new FormWithoutEqualsMethod("value1"); + + assertTrue(descriptor.areEqual(firstEntity, secondEntity)); + } + @Test public void testNullPropertyType() { JsonTypeDescriptor descriptor = new JsonTypeDescriptor(); @@ -120,4 +153,40 @@ public int hashCode() { return Objects.hash(formFields); } } + + private static class FormImpl extends AbstractForm { + private FormImpl(String value) { + super(value); + } + } + + private static abstract class AbstractForm { + private String value; + + private AbstractForm(String value) { + this.value = value; + } + + @Override + public boolean equals(Object o) { + return true; + } + + @Override + public int hashCode() { + return Objects.hashCode(value); + } + } + + private static class FormWithoutEqualsMethod { + private String value; + + public String getValue() { + return value; + } + + private FormWithoutEqualsMethod(String value) { + this.value = value; + } + } } diff --git a/hypersistence-utils-hibernate-55/src/main/java/io/hypersistence/utils/hibernate/type/json/internal/JsonTypeDescriptor.java b/hypersistence-utils-hibernate-55/src/main/java/io/hypersistence/utils/hibernate/type/json/internal/JsonTypeDescriptor.java index 38c641cb4..d398242ac 100644 --- a/hypersistence-utils-hibernate-55/src/main/java/io/hypersistence/utils/hibernate/type/json/internal/JsonTypeDescriptor.java +++ b/hypersistence-utils-hibernate-55/src/main/java/io/hypersistence/utils/hibernate/type/json/internal/JsonTypeDescriptor.java @@ -97,9 +97,11 @@ public boolean areEqual(Object one, Object another) { (one instanceof Map && another instanceof Map)) { return Objects.equals(one, another); } - if (one.getClass().equals(another.getClass()) && - ReflectionUtils.getDeclaredMethodOrNull(one.getClass(), "equals", Object.class) != null) { - return one.equals(another); + if (one.getClass().equals(another.getClass())) { + Method equalsMethod = ReflectionUtils.getMethodOrNull(one.getClass(), "equals", Object.class); + if (equalsMethod != null && !Object.class.equals(equalsMethod.getDeclaringClass())) { + return one.equals(another); + } } return objectMapperWrapper.toJsonNode(objectMapperWrapper.toString(one)).equals( objectMapperWrapper.toJsonNode(objectMapperWrapper.toString(another)) diff --git a/hypersistence-utils-hibernate-55/src/test/java/io/hypersistence/utils/hibernate/type/json/internal/JsonTypeDescriptorTest.java b/hypersistence-utils-hibernate-55/src/test/java/io/hypersistence/utils/hibernate/type/json/internal/JsonTypeDescriptorTest.java index 6115f3c2c..556da3917 100644 --- a/hypersistence-utils-hibernate-55/src/test/java/io/hypersistence/utils/hibernate/type/json/internal/JsonTypeDescriptorTest.java +++ b/hypersistence-utils-hibernate-55/src/test/java/io/hypersistence/utils/hibernate/type/json/internal/JsonTypeDescriptorTest.java @@ -42,6 +42,39 @@ public void testSetsAreEqual() { assertTrue(descriptor.areEqual(theFirst, theSecond)); } + /** + * When the JSON object contains an explicit equals method, even when defined on the abstract super class {@link AbstractForm}, + * that equals method should be used. + * If JSON serialization is used, + * the {@link JsonTypeDescriptor#areEqual(Object, Object)} depends on the values of the fields. + * + * The equals method of the {@link FormImpl} always returns true, so the two objects should be equal, + * even when having a different value. + */ + @Test + public void testAbstractClassImplementationsAreEqual() { + JsonTypeDescriptor descriptor = new JsonTypeDescriptor(); + + FormImpl firstEntity = new FormImpl("value1"); + FormImpl secondEntity = new FormImpl("value2"); + + assertTrue(descriptor.areEqual(firstEntity, secondEntity)); + } + + /** + * When the JSON object does not contain an explicit equals method, it should not use the equals method {@link Object}, + * but should use the ObjectWrapperMapper to equal the objects as JsonNodes + */ + @Test + public void testClassesWithoutEqualsMethodShouldEqualAsJsonNodes() { + JsonTypeDescriptor descriptor = new JsonTypeDescriptor(); + + FormWithoutEqualsMethod firstEntity = new FormWithoutEqualsMethod("value1"); + FormWithoutEqualsMethod secondEntity = new FormWithoutEqualsMethod("value1"); + + assertTrue(descriptor.areEqual(firstEntity, secondEntity)); + } + @Test public void testNullPropertyType() { JsonTypeDescriptor descriptor = new JsonTypeDescriptor(); @@ -120,4 +153,40 @@ public int hashCode() { return Objects.hash(formFields); } } + + private static class FormImpl extends AbstractForm { + private FormImpl(String value) { + super(value); + } + } + + private static abstract class AbstractForm { + private String value; + + private AbstractForm(String value) { + this.value = value; + } + + @Override + public boolean equals(Object o) { + return true; + } + + @Override + public int hashCode() { + return Objects.hashCode(value); + } + } + + private static class FormWithoutEqualsMethod { + private String value; + + public String getValue() { + return value; + } + + private FormWithoutEqualsMethod(String value) { + this.value = value; + } + } } diff --git a/hypersistence-utils-hibernate-60/src/main/java/io/hypersistence/utils/hibernate/type/json/internal/JsonJavaTypeDescriptor.java b/hypersistence-utils-hibernate-60/src/main/java/io/hypersistence/utils/hibernate/type/json/internal/JsonJavaTypeDescriptor.java index 4e2a315c0..13aa5246f 100644 --- a/hypersistence-utils-hibernate-60/src/main/java/io/hypersistence/utils/hibernate/type/json/internal/JsonJavaTypeDescriptor.java +++ b/hypersistence-utils-hibernate-60/src/main/java/io/hypersistence/utils/hibernate/type/json/internal/JsonJavaTypeDescriptor.java @@ -104,9 +104,11 @@ public boolean areEqual(Object one, Object another) { (one instanceof Map && another instanceof Map)) { return Objects.equals(one, another); } - if (one.getClass().equals(another.getClass()) && - ReflectionUtils.getDeclaredMethodOrNull(one.getClass(), "equals", Object.class) != null) { - return one.equals(another); + if (one.getClass().equals(another.getClass())) { + var equalsMethod = ReflectionUtils.getMethodOrNull(one.getClass(), "equals", Object.class); + if (equalsMethod != null && !Object.class.equals(equalsMethod.getDeclaringClass())) { + return one.equals(another); + } } return objectMapperWrapper.toJsonNode(objectMapperWrapper.toString(one)).equals( objectMapperWrapper.toJsonNode(objectMapperWrapper.toString(another)) diff --git a/hypersistence-utils-hibernate-60/src/test/java/io/hypersistence/utils/hibernate/type/json/internal/JsonTypeDescriptorTest.java b/hypersistence-utils-hibernate-60/src/test/java/io/hypersistence/utils/hibernate/type/json/internal/JsonTypeDescriptorTest.java index cc613faf2..aba9562a1 100644 --- a/hypersistence-utils-hibernate-60/src/test/java/io/hypersistence/utils/hibernate/type/json/internal/JsonTypeDescriptorTest.java +++ b/hypersistence-utils-hibernate-60/src/test/java/io/hypersistence/utils/hibernate/type/json/internal/JsonTypeDescriptorTest.java @@ -42,6 +42,39 @@ public void testSetsAreEqual() { assertTrue(descriptor.areEqual(theFirst, theSecond)); } + /** + * When the JSON object contains an explicit equals method, even when defined on the abstract super class {@link AbstractForm}, + * that equals method should be used. + * If JSON serialization is used, + * the {@link JsonJavaTypeDescriptor#areEqual(Object, Object)} depends on the values of the fields. + * + * The equals method of the {@link FormImpl} always returns true, so the two objects should be equal, + * even when having a different value. + */ + @Test + public void testAbstractClassImplementationsAreEqual() { + JsonJavaTypeDescriptor descriptor = new JsonJavaTypeDescriptor(); + + FormImpl firstEntity = new FormImpl("value1"); + FormImpl secondEntity = new FormImpl("value2"); + + assertTrue(descriptor.areEqual(firstEntity, secondEntity)); + } + + /** + * When the JSON object does not contain an explicit equals method, it should not use the equals method {@link Object}, + * but should use the ObjectWrapperMapper to equal the objects as JsonNodes + */ + @Test + public void testClassesWithoutEqualsMethodShouldEqualAsJsonNodes() { + JsonJavaTypeDescriptor descriptor = new JsonJavaTypeDescriptor(); + + FormWithoutEqualsMethod firstEntity = new FormWithoutEqualsMethod("value1"); + FormWithoutEqualsMethod secondEntity = new FormWithoutEqualsMethod("value1"); + + assertTrue(descriptor.areEqual(firstEntity, secondEntity)); + } + @Test public void testNullPropertyType() { JsonJavaTypeDescriptor descriptor = new JsonJavaTypeDescriptor(); @@ -120,4 +153,40 @@ public int hashCode() { return Objects.hash(formFields); } } + + private static class FormImpl extends AbstractForm { + private FormImpl(String value) { + super(value); + } + } + + private static abstract class AbstractForm { + private String value; + + private AbstractForm(String value) { + this.value = value; + } + + @Override + public boolean equals(Object o) { + return true; + } + + @Override + public int hashCode() { + return Objects.hashCode(value); + } + } + + private static class FormWithoutEqualsMethod { + private String value; + + public String getValue() { + return value; + } + + private FormWithoutEqualsMethod(String value) { + this.value = value; + } + } } diff --git a/hypersistence-utils-hibernate-62/src/main/java/io/hypersistence/utils/hibernate/type/json/internal/JsonJavaTypeDescriptor.java b/hypersistence-utils-hibernate-62/src/main/java/io/hypersistence/utils/hibernate/type/json/internal/JsonJavaTypeDescriptor.java index 4e2a315c0..13aa5246f 100644 --- a/hypersistence-utils-hibernate-62/src/main/java/io/hypersistence/utils/hibernate/type/json/internal/JsonJavaTypeDescriptor.java +++ b/hypersistence-utils-hibernate-62/src/main/java/io/hypersistence/utils/hibernate/type/json/internal/JsonJavaTypeDescriptor.java @@ -104,9 +104,11 @@ public boolean areEqual(Object one, Object another) { (one instanceof Map && another instanceof Map)) { return Objects.equals(one, another); } - if (one.getClass().equals(another.getClass()) && - ReflectionUtils.getDeclaredMethodOrNull(one.getClass(), "equals", Object.class) != null) { - return one.equals(another); + if (one.getClass().equals(another.getClass())) { + var equalsMethod = ReflectionUtils.getMethodOrNull(one.getClass(), "equals", Object.class); + if (equalsMethod != null && !Object.class.equals(equalsMethod.getDeclaringClass())) { + return one.equals(another); + } } return objectMapperWrapper.toJsonNode(objectMapperWrapper.toString(one)).equals( objectMapperWrapper.toJsonNode(objectMapperWrapper.toString(another)) diff --git a/hypersistence-utils-hibernate-62/src/test/java/io/hypersistence/utils/hibernate/type/json/internal/JsonTypeDescriptorTest.java b/hypersistence-utils-hibernate-62/src/test/java/io/hypersistence/utils/hibernate/type/json/internal/JsonTypeDescriptorTest.java index cc613faf2..aba9562a1 100644 --- a/hypersistence-utils-hibernate-62/src/test/java/io/hypersistence/utils/hibernate/type/json/internal/JsonTypeDescriptorTest.java +++ b/hypersistence-utils-hibernate-62/src/test/java/io/hypersistence/utils/hibernate/type/json/internal/JsonTypeDescriptorTest.java @@ -42,6 +42,39 @@ public void testSetsAreEqual() { assertTrue(descriptor.areEqual(theFirst, theSecond)); } + /** + * When the JSON object contains an explicit equals method, even when defined on the abstract super class {@link AbstractForm}, + * that equals method should be used. + * If JSON serialization is used, + * the {@link JsonJavaTypeDescriptor#areEqual(Object, Object)} depends on the values of the fields. + * + * The equals method of the {@link FormImpl} always returns true, so the two objects should be equal, + * even when having a different value. + */ + @Test + public void testAbstractClassImplementationsAreEqual() { + JsonJavaTypeDescriptor descriptor = new JsonJavaTypeDescriptor(); + + FormImpl firstEntity = new FormImpl("value1"); + FormImpl secondEntity = new FormImpl("value2"); + + assertTrue(descriptor.areEqual(firstEntity, secondEntity)); + } + + /** + * When the JSON object does not contain an explicit equals method, it should not use the equals method {@link Object}, + * but should use the ObjectWrapperMapper to equal the objects as JsonNodes + */ + @Test + public void testClassesWithoutEqualsMethodShouldEqualAsJsonNodes() { + JsonJavaTypeDescriptor descriptor = new JsonJavaTypeDescriptor(); + + FormWithoutEqualsMethod firstEntity = new FormWithoutEqualsMethod("value1"); + FormWithoutEqualsMethod secondEntity = new FormWithoutEqualsMethod("value1"); + + assertTrue(descriptor.areEqual(firstEntity, secondEntity)); + } + @Test public void testNullPropertyType() { JsonJavaTypeDescriptor descriptor = new JsonJavaTypeDescriptor(); @@ -120,4 +153,40 @@ public int hashCode() { return Objects.hash(formFields); } } + + private static class FormImpl extends AbstractForm { + private FormImpl(String value) { + super(value); + } + } + + private static abstract class AbstractForm { + private String value; + + private AbstractForm(String value) { + this.value = value; + } + + @Override + public boolean equals(Object o) { + return true; + } + + @Override + public int hashCode() { + return Objects.hashCode(value); + } + } + + private static class FormWithoutEqualsMethod { + private String value; + + public String getValue() { + return value; + } + + private FormWithoutEqualsMethod(String value) { + this.value = value; + } + } } diff --git a/hypersistence-utils-hibernate-63/src/main/java/io/hypersistence/utils/hibernate/type/json/internal/JsonJavaTypeDescriptor.java b/hypersistence-utils-hibernate-63/src/main/java/io/hypersistence/utils/hibernate/type/json/internal/JsonJavaTypeDescriptor.java index 4e2a315c0..13aa5246f 100644 --- a/hypersistence-utils-hibernate-63/src/main/java/io/hypersistence/utils/hibernate/type/json/internal/JsonJavaTypeDescriptor.java +++ b/hypersistence-utils-hibernate-63/src/main/java/io/hypersistence/utils/hibernate/type/json/internal/JsonJavaTypeDescriptor.java @@ -104,9 +104,11 @@ public boolean areEqual(Object one, Object another) { (one instanceof Map && another instanceof Map)) { return Objects.equals(one, another); } - if (one.getClass().equals(another.getClass()) && - ReflectionUtils.getDeclaredMethodOrNull(one.getClass(), "equals", Object.class) != null) { - return one.equals(another); + if (one.getClass().equals(another.getClass())) { + var equalsMethod = ReflectionUtils.getMethodOrNull(one.getClass(), "equals", Object.class); + if (equalsMethod != null && !Object.class.equals(equalsMethod.getDeclaringClass())) { + return one.equals(another); + } } return objectMapperWrapper.toJsonNode(objectMapperWrapper.toString(one)).equals( objectMapperWrapper.toJsonNode(objectMapperWrapper.toString(another)) diff --git a/hypersistence-utils-hibernate-63/src/test/java/io/hypersistence/utils/hibernate/type/json/internal/JsonTypeDescriptorTest.java b/hypersistence-utils-hibernate-63/src/test/java/io/hypersistence/utils/hibernate/type/json/internal/JsonTypeDescriptorTest.java index cc613faf2..aba9562a1 100644 --- a/hypersistence-utils-hibernate-63/src/test/java/io/hypersistence/utils/hibernate/type/json/internal/JsonTypeDescriptorTest.java +++ b/hypersistence-utils-hibernate-63/src/test/java/io/hypersistence/utils/hibernate/type/json/internal/JsonTypeDescriptorTest.java @@ -42,6 +42,39 @@ public void testSetsAreEqual() { assertTrue(descriptor.areEqual(theFirst, theSecond)); } + /** + * When the JSON object contains an explicit equals method, even when defined on the abstract super class {@link AbstractForm}, + * that equals method should be used. + * If JSON serialization is used, + * the {@link JsonJavaTypeDescriptor#areEqual(Object, Object)} depends on the values of the fields. + * + * The equals method of the {@link FormImpl} always returns true, so the two objects should be equal, + * even when having a different value. + */ + @Test + public void testAbstractClassImplementationsAreEqual() { + JsonJavaTypeDescriptor descriptor = new JsonJavaTypeDescriptor(); + + FormImpl firstEntity = new FormImpl("value1"); + FormImpl secondEntity = new FormImpl("value2"); + + assertTrue(descriptor.areEqual(firstEntity, secondEntity)); + } + + /** + * When the JSON object does not contain an explicit equals method, it should not use the equals method {@link Object}, + * but should use the ObjectWrapperMapper to equal the objects as JsonNodes + */ + @Test + public void testClassesWithoutEqualsMethodShouldEqualAsJsonNodes() { + JsonJavaTypeDescriptor descriptor = new JsonJavaTypeDescriptor(); + + FormWithoutEqualsMethod firstEntity = new FormWithoutEqualsMethod("value1"); + FormWithoutEqualsMethod secondEntity = new FormWithoutEqualsMethod("value1"); + + assertTrue(descriptor.areEqual(firstEntity, secondEntity)); + } + @Test public void testNullPropertyType() { JsonJavaTypeDescriptor descriptor = new JsonJavaTypeDescriptor(); @@ -120,4 +153,40 @@ public int hashCode() { return Objects.hash(formFields); } } + + private static class FormImpl extends AbstractForm { + private FormImpl(String value) { + super(value); + } + } + + private static abstract class AbstractForm { + private String value; + + private AbstractForm(String value) { + this.value = value; + } + + @Override + public boolean equals(Object o) { + return true; + } + + @Override + public int hashCode() { + return Objects.hashCode(value); + } + } + + private static class FormWithoutEqualsMethod { + private String value; + + public String getValue() { + return value; + } + + private FormWithoutEqualsMethod(String value) { + this.value = value; + } + } }