8038999: In Java 8 java.awt.datatransfer.DataFlavor.equals is no longer symmetric
Reviewed-by: anthony, serb
--- 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);
+ }
+ }
+}