From d2a858e173ae10c577d7a3d14eb87ab5c3f60062 Mon Sep 17 00:00:00 2001 From: Prasanta Sadhukhan Date: Mon, 19 Jun 2023 08:52:06 +0000 Subject: [PATCH] 7083187: Class CSS.CssValue is missing implementations of equals() and hashCode() Co-authored-by: Alexey Ivanov Reviewed-by: aivanov, prr --- .../classes/javax/swing/text/html/CSS.java | 116 ++++++++++++++ .../html/CSS/CSSAttributeEqualityBug.java | 148 ++++++++++++++++++ 2 files changed, 264 insertions(+) create mode 100644 test/jdk/javax/swing/text/html/CSS/CSSAttributeEqualityBug.java diff --git a/src/java.desktop/share/classes/javax/swing/text/html/CSS.java b/src/java.desktop/share/classes/javax/swing/text/html/CSS.java index 59ddf4856b2..bb60b382654 100644 --- a/src/java.desktop/share/classes/javax/swing/text/html/CSS.java +++ b/src/java.desktop/share/classes/javax/swing/text/html/CSS.java @@ -38,6 +38,7 @@ import java.net.URL; import java.util.ArrayList; import java.util.Enumeration; import java.util.Hashtable; +import java.util.Objects; import javax.swing.ImageIcon; import javax.swing.SizeRequirements; @@ -2024,6 +2025,18 @@ public class CSS implements Serializable { boolean isSup() { return (svalue.contains("sup")); } + + @Override + public int hashCode() { + return (this.svalue != null) ? this.svalue.hashCode() : 0; + } + + @Override + public boolean equals(Object val) { + return val instanceof CSS.StringValue strVal + && Objects.equals(this.svalue, strVal.svalue); + } + } /** @@ -2201,6 +2214,21 @@ public class CSS implements Serializable { return Integer.valueOf(getValue(null, null)); } + @Override + public int hashCode() { + return Float.hashCode(value) + | Boolean.hashCode(index) + | Objects.hashCode(lu); + } + + @Override + public boolean equals(Object val) { + return val instanceof CSS.FontSize size + && value == size.value + && index == size.index + && Objects.equals(lu, size.lu); + } + float value; boolean index; LengthUnit lu; @@ -2301,6 +2329,17 @@ public class CSS implements Serializable { return family; } + @Override + public int hashCode() { + return (family != null) ? family.hashCode() : 0; + } + + @Override + public boolean equals(Object val) { + return val instanceof CSS.FontFamily font + && Objects.equals(family, font.family); + } + String family; } @@ -2364,6 +2403,17 @@ public class CSS implements Serializable { return (weight > 500); } + @Override + public int hashCode() { + return Integer.hashCode(weight); + } + + @Override + public boolean equals(Object val) { + return val instanceof CSS.FontWeight fontWeight + && weight == fontWeight.weight; + } + int weight; } @@ -2424,6 +2474,16 @@ public class CSS implements Serializable { return c; } + @Override + public int hashCode() { + return (c != null) ? c.hashCode() : 0; + } + + @Override + public boolean equals(Object val) { + return val instanceof CSS.ColorValue color && c.equals(color.c); + } + Color c; } @@ -2478,6 +2538,16 @@ public class CSS implements Serializable { } } + @Override + public int hashCode() { + return (style != null) ? style.hashCode() : 0; + } + + @Override + public boolean equals(Object val) { + return val instanceof CSS.BorderStyle border && style.equals(border.style); + } + // CSS.Values are static, don't archive it. private transient CSS.Value style; } @@ -2605,9 +2675,25 @@ public class CSS implements Serializable { return Float.valueOf(getValue(false)); } + @Override + public int hashCode() { + return Float.hashCode(span) + | Boolean.hashCode(percentage) + | Objects.hashCode(units); + } + + @Override + public boolean equals(Object val) { + return val instanceof CSS.LengthValue lu + && percentage == lu.percentage + && span == lu.span + && Objects.equals(units, lu.units); + } + /** If true, span is a percentage value, and that to determine * the length another value needs to be passed in. */ boolean percentage; + /** Either the absolute value (percentage == false) or * a percentage value. */ float span; @@ -2824,6 +2910,21 @@ public class CSS implements Serializable { float getVerticalPosition() { return verticalPosition; } + + @Override + public int hashCode() { + return Float.hashCode(horizontalPosition) + | Float.hashCode(verticalPosition) + | Short.hashCode(relative); + } + + @Override + public boolean equals(Object val) { + return val instanceof CSS.BackgroundPosition bp + && horizontalPosition == bp.horizontalPosition + && verticalPosition == bp.verticalPosition + && relative == bp.relative; + } } @@ -2983,6 +3084,21 @@ public class CSS implements Serializable { return type + " " + value; } + @Override + public int hashCode() { + return Float.hashCode(value) + | Short.hashCode(type) + | Objects.hashCode(units); + } + + @Override + public boolean equals(Object obj) { + return obj instanceof LengthUnit lu + && type == lu.type + && value == lu.value + && Objects.equals(units, lu.units); + } + // 0 - value indicates real value // 1 - % value, value relative to depends upon key. // 50% will have a value = .5 diff --git a/test/jdk/javax/swing/text/html/CSS/CSSAttributeEqualityBug.java b/test/jdk/javax/swing/text/html/CSS/CSSAttributeEqualityBug.java new file mode 100644 index 00000000000..7968f773bd5 --- /dev/null +++ b/test/jdk/javax/swing/text/html/CSS/CSSAttributeEqualityBug.java @@ -0,0 +1,148 @@ +/* + * Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Objects; + +import javax.swing.text.AttributeSet; +import javax.swing.text.html.StyleSheet; + +/* + * @test + * @bug 7083187 + * @summary Verifies if CSS.CSSValue attribute is same + * @run main CSSAttributeEqualityBug + */ +public class CSSAttributeEqualityBug { + + /** + * CSS declarations which should produce equal attribute sets. + */ + private static final String[] EQUALS = { + "font-size: 42", + "font-size: 42px", + "font-size: 42em", + "font-size: medium", + "font-size: smaller", + "font-size: 200%", + + "font-family: sans-serif", + "font-family: 'DejaVu Serif', serif", + + "font-weight: bold", + + "color: red", + "color: rgb(255, 0, 0)", + + "border-style: dashed", + + "margin-top: 42", + "margin-top: 42px", + "margin-top: 100%", + + "text-decoration: underline", + + "background-position: top", + "background-position: top right", + "background-position: 25% 75%", + "background-position: 0 0", + "background-position: 1cm 2cm", + "background-position: 1em 2em", + }; + + /** + * CSS declarations which should produce different attribute sets. + */ + private static final String[][] NOT_EQUALS = { + {"font-size: 42px", "font-size: 22px"}, + {"font-size: 42px", "font-size: 42pt"}, + {"font-size: 42em", "font-size: 42ex"}, + {"font-size: 100%", "font-size: 200%"}, + + {"margin-top: 42px", "margin-top: 22px"}, + {"margin-top: 42px", "margin-top: 42pt"}, + {"margin-top: 100%", "margin-top: 50%"}, + }; + + public static void main(String[] args) { + final List failures = new ArrayList<>(); + + Arrays.stream(EQUALS) + .map(CSSAttributeEqualityBug::positiveTest) + .filter(Objects::nonNull) + .forEach(failures::add); + Arrays.stream(NOT_EQUALS) + .map(CSSAttributeEqualityBug::negativeTest) + .filter(Objects::nonNull) + .forEach(failures::add); + + if (!failures.isEmpty()) { + failures.forEach(System.err::println); + throw new RuntimeException(failures.size() + + " failure(s) detected: " + + failures.get(0)); + } + } + + private static String positiveTest(String cssDeclaration) { + StyleSheet ss = new StyleSheet(); + + AttributeSet a = ss.getDeclaration(cssDeclaration); + AttributeSet b = ss.getDeclaration(cssDeclaration); + + return assertEquals(a, b); + } + + private static String negativeTest(String[] cssDeclaration) { + StyleSheet ss = new StyleSheet(); + + AttributeSet a = ss.getDeclaration(cssDeclaration[0]); + AttributeSet b = ss.getDeclaration(cssDeclaration[1]); + + return assertNotEquals(a, b); + } + + private static String assertEquals(AttributeSet a, + AttributeSet b) { + return !a.isEqual(b) + ? getErrorMessage(a, b, "is not equal to") + : null; + } + + private static String assertNotEquals(AttributeSet a, + AttributeSet b) { + return a.isEqual(b) + ? getErrorMessage(a, b, "is equal to") + : null; + } + + private static String getErrorMessage(AttributeSet a, + AttributeSet b, + String message) { + return a + " " + message + " " + b; + } + +} +