Skip to content

Commit 5c030cc

Browse files
desruisseauxjayathirthrao
authored andcommitted
8290973: In AffineTransform, equals(Object) is inconsistent with hashCode()
Reviewed-by: prr
1 parent f888aa9 commit 5c030cc

File tree

2 files changed

+108
-8
lines changed

2 files changed

+108
-8
lines changed

src/java.desktop/share/classes/java/awt/geom/AffineTransform.java

Lines changed: 27 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3902,15 +3902,25 @@ public Object clone() {
39023902
* @since 1.2
39033903
*/
39043904
public int hashCode() {
3905-
long bits = Double.doubleToLongBits(m00);
3906-
bits = bits * 31 + Double.doubleToLongBits(m01);
3907-
bits = bits * 31 + Double.doubleToLongBits(m02);
3908-
bits = bits * 31 + Double.doubleToLongBits(m10);
3909-
bits = bits * 31 + Double.doubleToLongBits(m11);
3910-
bits = bits * 31 + Double.doubleToLongBits(m12);
3905+
long bits = hash(m00);
3906+
bits = bits * 31 + hash(m01);
3907+
bits = bits * 31 + hash(m02);
3908+
bits = bits * 31 + hash(m10);
3909+
bits = bits * 31 + hash(m11);
3910+
bits = bits * 31 + hash(m12);
39113911
return (((int) bits) ^ ((int) (bits >> 32)));
39123912
}
39133913

3914+
/**
3915+
* Returns a hash code for the given value, with negative zero
3916+
* collapsed to the single positive zero.
3917+
*/
3918+
private static long hash(double m) {
3919+
long h = Double.doubleToLongBits(m);
3920+
if (h == 0x8000000000000000L) h = 0; // Replace -0 by +0.
3921+
return h;
3922+
}
3923+
39143924
/**
39153925
* Returns {@code true} if this {@code AffineTransform}
39163926
* represents the same affine coordinate transform as the specified
@@ -3928,8 +3938,17 @@ public boolean equals(Object obj) {
39283938

39293939
AffineTransform a = (AffineTransform)obj;
39303940

3931-
return ((m00 == a.m00) && (m01 == a.m01) && (m02 == a.m02) &&
3932-
(m10 == a.m10) && (m11 == a.m11) && (m12 == a.m12));
3941+
return equals(m00, a.m00) && equals(m01, a.m01) &&
3942+
equals(m02, a.m02) && equals(m10, a.m10) &&
3943+
equals(m11, a.m11) && equals(m12, a.m12);
3944+
}
3945+
3946+
/**
3947+
* Compares the given floating point values, with negative zero
3948+
* considered equals to positive zero.
3949+
*/
3950+
private static boolean equals(double a, double b) {
3951+
return (a == b) || (Double.isNaN(a) && Double.isNaN(b));
39333952
}
39343953

39353954
/* Serialization support. A readObject method is neccessary because
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
/*
2+
* Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved.
3+
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4+
*
5+
* This code is free software; you can redistribute it and/or modify it
6+
* under the terms of the GNU General Public License version 2 only, as
7+
* published by the Free Software Foundation.
8+
*
9+
* This code is distributed in the hope that it will be useful, but WITHOUT
10+
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11+
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
12+
* version 2 for more details (a copy is included in the LICENSE file that
13+
* accompanied this code).
14+
*
15+
* You should have received a copy of the GNU General Public License version
16+
* 2 along with this work; if not, write to the Free Software Foundation,
17+
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18+
*
19+
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20+
* or visit www.oracle.com if you need additional information or have any
21+
* questions.
22+
*/
23+
24+
/**
25+
* @test
26+
* @summary Tests that equals(Object) is consistent with hashCode(),
27+
* in particular regarding negative versus positive zeros and
28+
* NaN values.
29+
*/
30+
31+
import java.awt.geom.AffineTransform;
32+
33+
public class EqualsAndHashCode {
34+
private static boolean failed;
35+
36+
public static void main(String arg[]) {
37+
checkReflexiveEquals();
38+
checkZeros();
39+
checkNotEqual();
40+
if (failed) {
41+
throw new RuntimeException("Some tests failed.");
42+
}
43+
}
44+
45+
private static void checkReflexiveEquals() {
46+
AffineTransform t = new AffineTransform(1, 0, 0, 1, Double.NaN, 0);
47+
if (!t.equals(t)) {
48+
System.err.println("Transform should be equal to itself.");
49+
failed = true;
50+
}
51+
if (!t.equals(t.clone())) {
52+
System.err.println("Transform should be equal to its clone.");
53+
failed = true;
54+
}
55+
}
56+
57+
private static void checkZeros() {
58+
AffineTransform positive = new AffineTransform(2, 0, 0, 3, 0, +0.0);
59+
AffineTransform negative = new AffineTransform(2, 0, 0, 3, 0, -0.0);
60+
if (!positive.equals(negative)) {
61+
System.err.println("Transforms should be equal despite the sign difference in zero values.");
62+
failed = true;
63+
} else if (positive.hashCode() != negative.hashCode()) {
64+
System.err.println("Equal transforms should have the same hash code value.");
65+
failed = true;
66+
}
67+
}
68+
69+
private static void checkNotEqual() {
70+
AffineTransform t1 = new AffineTransform(2, 0, 0, 3, 2, 0);
71+
AffineTransform t2 = new AffineTransform(2, 0, 0, 3, 2, 4);
72+
if (t1.equals(t2)) {
73+
System.err.println("Expected non-equal transforms.");
74+
failed = true;
75+
}
76+
if (t1.hashCode() == t2.hashCode()) {
77+
System.err.println("Expected different hash codes.");
78+
failed = true;
79+
}
80+
}
81+
}

0 commit comments

Comments
 (0)