8038999: In Java 8 java.awt.datatransfer.DataFlavor.equals is no longer symmetric
authorpchelko
Thu, 03 Apr 2014 13:02:39 +0400
changeset 24156 818b357f7778
parent 24155 53e618ceb567
child 24157 84ccec128c31
8038999: In Java 8 java.awt.datatransfer.DataFlavor.equals is no longer symmetric Reviewed-by: anthony, serb
jdk/src/share/classes/java/awt/datatransfer/DataFlavor.java
jdk/test/java/awt/datatransfer/DataFlavor/EqualsHashCodeSymmetryTest/EqualsHashCodeSymmetryTest.java
--- a/jdk/src/share/classes/java/awt/datatransfer/DataFlavor.java	Thu Apr 03 11:19:10 2014 +0400
+++ b/jdk/src/share/classes/java/awt/datatransfer/DataFlavor.java	Thu Apr 03 13:02:39 2014 +0400
@@ -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 @@
     * @throws ClassNotFoundException
     * @throws  NullPointerException if <code>mimeType</code> is null
     *
-    * @see tryToLoadClass
+    * @see #tryToLoadClass
     */
     private void initialize(String mimeType, String humanPresentableName, ClassLoader classLoader) throws MimeTypeParseException, ClassNotFoundException {
         if (mimeType == null) {
@@ -986,14 +1001,8 @@
             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) {
@@ -1006,34 +1015,22 @@
             }
 
             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;
                     }
                 }
@@ -1090,18 +1087,21 @@
             // 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();
+                    }
                 }
             }
         }
@@ -1177,6 +1177,20 @@
         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 <code>DataFlavor</code> represent a serialized object?
     * @return whether or not a serialized object is represented
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/awt/datatransfer/DataFlavor/EqualsHashCodeSymmetryTest/EqualsHashCodeSymmetryTest.java	Thu Apr 03 13:02:39 2014 +0400
@@ -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 <petr.pchelko@oracle.com>
+ */
+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);
+        }
+    }
+}