From b5520f2219bc8fa1ac6fc0099c76c35eb8d2ef8d Mon Sep 17 00:00:00 2001 From: Andreas Stefl Date: Mon, 5 Oct 2026 03:18:06 +0200 Subject: [PATCH] fix(java): align floating value equality with hashing --- CHANGELOG.md | 3 +++ .../app/opendocument/core/DrawingPath.java | 8 +++---- .../opendocument/core/DrawingTransform.java | 8 +++---- jni/java/app/opendocument/core/Measure.java | 2 +- jni/tests/app/opendocument/core/MetaTest.java | 22 +++++++++++++++++++ 5 files changed, 34 insertions(+), 9 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a96ebf266..9cd99a462 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,9 @@ The release run heads these entries with the version and opens a fresh ## Unreleased +- Java `Measure`, `DrawingPath` and `DrawingTransform` equality now agrees with + hashing for signed zero and is reflexive for NaN, following `Double` semantics. + - Java bindings release native handles on failed allocations, avoid pinned-array leaks, and check array sizes before passing them to the JVM. diff --git a/jni/java/app/opendocument/core/DrawingPath.java b/jni/java/app/opendocument/core/DrawingPath.java index 572d052ca..223ca9d5d 100644 --- a/jni/java/app/opendocument/core/DrawingPath.java +++ b/jni/java/app/opendocument/core/DrawingPath.java @@ -22,10 +22,10 @@ public DrawingPath(String data, double x, double y, double width, double height) public boolean equals(Object other) { return other instanceof DrawingPath path && data.equals(path.data) - && x == path.x - && y == path.y - && width == path.width - && height == path.height; + && Double.compare(x, path.x) == 0 + && Double.compare(y, path.y) == 0 + && Double.compare(width, path.width) == 0 + && Double.compare(height, path.height) == 0; } @Override diff --git a/jni/java/app/opendocument/core/DrawingTransform.java b/jni/java/app/opendocument/core/DrawingTransform.java index 4ea09d7f0..3f5230e18 100644 --- a/jni/java/app/opendocument/core/DrawingTransform.java +++ b/jni/java/app/opendocument/core/DrawingTransform.java @@ -23,10 +23,10 @@ public DrawingTransform(double a, double b, double c, double d, Measure e, Measu @Override public boolean equals(Object other) { return other instanceof DrawingTransform transform - && a == transform.a - && b == transform.b - && c == transform.c - && d == transform.d + && Double.compare(a, transform.a) == 0 + && Double.compare(b, transform.b) == 0 + && Double.compare(c, transform.c) == 0 + && Double.compare(d, transform.d) == 0 && e.equals(transform.e) && f.equals(transform.f); } diff --git a/jni/java/app/opendocument/core/Measure.java b/jni/java/app/opendocument/core/Measure.java index 2cff05de3..206560ebb 100644 --- a/jni/java/app/opendocument/core/Measure.java +++ b/jni/java/app/opendocument/core/Measure.java @@ -15,7 +15,7 @@ public Measure(double magnitude, String unit) { @Override public boolean equals(Object other) { return other instanceof Measure measure - && magnitude == measure.magnitude + && Double.compare(magnitude, measure.magnitude) == 0 && unit.equals(measure.unit); } diff --git a/jni/tests/app/opendocument/core/MetaTest.java b/jni/tests/app/opendocument/core/MetaTest.java index 6179f92ee..c604a3b52 100644 --- a/jni/tests/app/opendocument/core/MetaTest.java +++ b/jni/tests/app/opendocument/core/MetaTest.java @@ -9,6 +9,7 @@ import java.util.HashSet; import java.util.List; import java.util.Set; +import java.util.function.DoubleFunction; import org.junit.jupiter.api.Test; class MetaTest { @@ -95,6 +96,27 @@ void capabilitiesByFileType() { assertFalse(pdf.colorScheme); } + @Test + void floatingValuesHaveConsistentEqualityAndHashes() { + List> values = List.of( + value -> new Measure(value, "pt"), + value -> new DrawingPath("M0 0", value, value, value, value), + value -> new DrawingTransform(value, value, value, value, + new Measure(0, "pt"), new Measure(0, "pt"))); + for (DoubleFunction value : values) { + Object nan = value.apply(Double.NaN); + Object sameNan = value.apply(Double.NaN); + assertTrue(nan.equals(nan)); + assertEquals(nan, sameNan); + assertEquals(nan.hashCode(), sameNan.hashCode()); + Object positiveZero = value.apply(0.0); + Object negativeZero = value.apply(-0.0); + assertFalse(positiveZero.equals(negativeZero)); + assertEquals(positiveZero, value.apply(0.0)); + assertEquals(positiveZero.hashCode(), value.apply(0.0).hashCode()); + } + } + @Test void tablePosition() { assertEquals(0, TablePosition.toColumnNum("A"));