From fc6f83bc1a4b8ea8252d59806cc3637d984e9945 Mon Sep 17 00:00:00 2001 From: pchelko Date: Thu, 3 Apr 2014 13:02:39 +0400 Subject: [PATCH] 8038999: In Java 8 java.awt.datatransfer.DataFlavor.equals is no longer symmetric Reviewed-by: anthony, serb --- .../java/awt/datatransfer/DataFlavor.java | 110 ++++++++++-------- .../EqualsHashCodeSymmetryTest.java | 88 ++++++++++++++ 2 files changed, 150 insertions(+), 48 deletions(-) create mode 100644 test/java/awt/datatransfer/DataFlavor/EqualsHashCodeSymmetryTest/EqualsHashCodeSymmetryTest.java diff --git a/src/share/classes/java/awt/datatransfer/DataFlavor.java b/src/share/classes/java/awt/datatransfer/DataFlavor.java index 9ca61a34a..2df8e5ac8 100644 --- a/src/share/classes/java/awt/datatransfer/DataFlavor.java +++ b/src/share/classes/java/awt/datatransfer/DataFlavor.java @@ -25,13 +25,28 @@ package java.awt.datatransfer; -import java.io.*; -import java.nio.*; -import java.util.*; - import sun.awt.datatransfer.DataTransferer; import sun.reflect.misc.ReflectUtil; +import java.io.ByteArrayInputStream; +import java.io.CharArrayReader; +import java.io.Externalizable; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.ObjectInput; +import java.io.ObjectOutput; +import java.io.OptionalDataException; +import java.io.Reader; +import java.io.StringReader; +import java.io.UnsupportedEncodingException; +import java.nio.ByteBuffer; +import java.nio.CharBuffer; +import java.util.Arrays; +import java.util.Collections; +import java.util.Comparator; +import java.util.Objects; + import static sun.security.util.SecurityConstants.GET_CLASSLOADER_PERMISSION; /** @@ -501,7 +516,7 @@ public class DataFlavor implements Externalizable, Cloneable { * @throws ClassNotFoundException * @throws NullPointerException if mimeType is null * - * @see tryToLoadClass + * @see #tryToLoadClass */ private void initialize(String mimeType, String humanPresentableName, ClassLoader classLoader) throws MimeTypeParseException, ClassNotFoundException { if (mimeType == null) { @@ -990,14 +1005,8 @@ public class DataFlavor implements Externalizable, Cloneable { return true; } - if (representationClass == null) { - if (that.getRepresentationClass() != null) { - return false; - } - } else { - if (!representationClass.equals(that.getRepresentationClass())) { - return false; - } + if (!Objects.equals(this.getRepresentationClass(), that.getRepresentationClass())) { + return false; } if (mimeType == null) { @@ -1010,34 +1019,22 @@ public class DataFlavor implements Externalizable, Cloneable { } if ("text".equals(getPrimaryType())) { - if (DataTransferer.doesSubtypeSupportCharset(this) && - representationClass != null && - !(isRepresentationClassReader() || - String.class.equals(representationClass) || - isRepresentationClassCharBuffer() || - char[].class.equals(representationClass))) - { + if (DataTransferer.doesSubtypeSupportCharset(this) + && representationClass != null + && !isStandardTextRepresentationClass()) { String thisCharset = - DataTransferer.canonicalName(getParameter("charset")); + DataTransferer.canonicalName(this.getParameter("charset")); String thatCharset = - DataTransferer.canonicalName(that.getParameter("charset")); - if (thisCharset == null) { - if (thatCharset != null) { - return false; - } - } else { - if (!thisCharset.equals(thatCharset)) { - return false; - } + DataTransferer.canonicalName(that.getParameter("charset")); + if (!Objects.equals(thisCharset, thatCharset)) { + return false; } } - if ("html".equals(getSubType()) && - this.getParameter("document") != null ) - { - if (!this.getParameter("document"). - equals(that.getParameter("document"))) - { + if ("html".equals(getSubType())) { + String thisDocument = this.getParameter("document"); + String thatDocument = that.getParameter("document"); + if (!Objects.equals(thisDocument, thatDocument)) { return false; } } @@ -1094,18 +1091,21 @@ public class DataFlavor implements Externalizable, Cloneable { // MimeType.match which reports a match if one or both of the // subTypes is '*', regardless of the other subType. - if ("text".equals(primaryType) && - DataTransferer.doesSubtypeSupportCharset(this) && - representationClass != null && - !(isRepresentationClassReader() || - String.class.equals(representationClass) || - isRepresentationClassCharBuffer() || - char[].class.equals(representationClass))) - { - String charset = - DataTransferer.canonicalName(getParameter("charset")); - if (charset != null) { - total += charset.hashCode(); + if ("text".equals(primaryType)) { + if (DataTransferer.doesSubtypeSupportCharset(this) + && representationClass != null + && !isStandardTextRepresentationClass()) { + String charset = DataTransferer.canonicalName(getParameter("charset")); + if (charset != null) { + total += charset.hashCode(); + } + } + + if ("html".equals(getSubType())) { + String document = this.getParameter("document"); + if (document != null) { + total += document.hashCode(); + } } } } @@ -1181,6 +1181,20 @@ public class DataFlavor implements Externalizable, Cloneable { return mimeType.match(mtype); } + /** + * Checks if the representation class is one of the standard text + * representation classes. + * + * @return true if the representation class is one of the standard text + * representation classes, otherwise false + */ + private boolean isStandardTextRepresentationClass() { + return isRepresentationClassReader() + || String.class.equals(representationClass) + || isRepresentationClassCharBuffer() + || char[].class.equals(representationClass); + } + /** * Does the DataFlavor represent a serialized object? */ diff --git a/test/java/awt/datatransfer/DataFlavor/EqualsHashCodeSymmetryTest/EqualsHashCodeSymmetryTest.java b/test/java/awt/datatransfer/DataFlavor/EqualsHashCodeSymmetryTest/EqualsHashCodeSymmetryTest.java new file mode 100644 index 000000000..bfe820f5c --- /dev/null +++ b/test/java/awt/datatransfer/DataFlavor/EqualsHashCodeSymmetryTest/EqualsHashCodeSymmetryTest.java @@ -0,0 +1,88 @@ +/* + * Copyright (c) 2014, 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.awt.datatransfer.DataFlavor; + +/** + * @test + * @bug 8038999 + * @summary DataFlavor.equals is not symmetric + * @author Petr Pchelko + */ +public class EqualsHashCodeSymmetryTest { + + private static final DataFlavor[] dataFlavors = { + DataFlavor.stringFlavor, + DataFlavor.imageFlavor, + DataFlavor.javaFileListFlavor, + DataFlavor.allHtmlFlavor, + DataFlavor.selectionHtmlFlavor, + DataFlavor.fragmentHtmlFlavor, + createFlavor("text/html; class=java.lang.String"), + new DataFlavor(String.class, "My test flavor number 1"), + new DataFlavor(String.class, "My test flavor number 2"), + new DataFlavor(StringBuilder.class, "My test flavor number 1") + }; + + public static void main(String[] args) { + testEqualsSymmetry(); + testEqualsHashCodeConsistency(); + testSimpleCollision(); + } + + private static void testEqualsSymmetry() { + for (DataFlavor flavor1 : dataFlavors) { + for (DataFlavor flavor2 : dataFlavors) { + if (flavor1.equals(flavor2) != flavor2.equals(flavor1)) { + throw new RuntimeException( + String.format("Equals is not symmetric for %s and %s", flavor1, flavor2)); + } + } + } + } + + private static void testEqualsHashCodeConsistency() { + for (DataFlavor flavor1 : dataFlavors) { + for (DataFlavor flavor2 : dataFlavors) { + if ((flavor1.equals(flavor2) && flavor1.hashCode() != flavor2.hashCode())) { + throw new RuntimeException( + String.format("Equals and hash code not consistent for %s and %s", flavor1, flavor2)); + } + } + } + } + + private static void testSimpleCollision() { + if (createFlavor("text/html; class=java.lang.String").hashCode() == DataFlavor.allHtmlFlavor.hashCode()) { + throw new RuntimeException("HashCode collision because the document parameter is not used"); + } + } + + private static DataFlavor createFlavor(String mime) { + try { + return new DataFlavor(mime); + } catch (ClassNotFoundException e) { + throw new RuntimeException(e); + } + } +} -- GitLab